diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/GSG/get_started.html b/GSG/get_started.html new file mode 100644 index 0000000000..9485df3d8a --- /dev/null +++ b/GSG/get_started.html @@ -0,0 +1,693 @@ + + + + + + + + + + + Get Started with oneAPI Threading Building Blocks (oneTBB) — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Get Started with oneAPI Threading Building Blocks (oneTBB)

+ +
+
+ +
+

Contents

+
+ +
+
+
+ + + + +
+ +
+

Get Started with oneAPI Threading Building Blocks (oneTBB)#

+

oneTBB Get Started Guide provides the information you need to begin working with oneTBB. +It is helpful for new users of parallel programming and experienced developers that want to improve code performance.

+

It is recommended for you to have a basic knowledge of C++ programming and some experience with parallel programming concepts.

+

oneAPI Threading Building Blocks (oneTBB) is a runtime-based parallel programming model for C++ code that uses tasks. +The template-based runtime library can help you harness the latent performance of multi-core processors.

+

oneTBB enables you to simplify parallel programming by breaking computation into parallel running tasks. Within a single process, +parallelism is carried out by mapping tasks to threads. Threads are an operating system mechanism that allows the same or different sets of instructions +to be executed simultaneously. Using threads can make your program work faster and more efficiently.

+

Here you can see one of the possible executions of tasks by threads.

+
+../_images/how-oneTBB-works.png + +
+

Use oneTBB to write scalable applications that:

+
    +
  • Specify logical parallel structure instead of threads.

  • +
  • Emphasize data-parallel programming.

  • +
  • Take advantage of concurrent collections and parallel algorithms.

  • +
+

oneTBB supports nested parallelism and load balancing. It means that you can use the library without worrying about oversubscribing a system, which happens when more tasks are assigned to a system than it can handle efficiently.

+

oneTBB is used in different areas, such as scientific simulations, gaming, data analysis, etc.

+

It is available as a stand-alone product and as part of the Intel® oneAPI Base Toolkit.

+
+

To start using oneTBB, follow the next steps:#

+
    +
  1. See the System Requirements.

  2. +
  3. Install oneTBB.

  4. +
  5. Run your program using oneTBB following the Next Steps.

  6. +
  7. Learn how to Integrate oneTBB into your project using CMake* and pkg-config tool.

  8. +
  9. See oneTBB Samples.

  10. +
+
+
+ + +
+ + + + + + + + +
+ + + + +
+ + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/GSG/installation.html b/GSG/installation.html new file mode 100644 index 0000000000..1289da1407 --- /dev/null +++ b/GSG/installation.html @@ -0,0 +1,637 @@ + + + + + + + + + + + Installation — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Installation

+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+ + + + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/GSG/integrate.html b/GSG/integrate.html new file mode 100644 index 0000000000..d3bcdf7372 --- /dev/null +++ b/GSG/integrate.html @@ -0,0 +1,713 @@ + + + + + + + + + + + Integrate oneTBB — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

Integrate oneTBB

+ +
+
+ +
+

Contents

+
+ +
+
+
+ + + + +
+ +
+

Integrate oneTBB#

+

If you want to improve the performance and scalability of your application, you can integrate oneTBB into your project. +For example, you may want to integrate oneTBB if your application needs to process large amounts of data in parallel.

+

To integrate oneTBB, you need to:

+
    +
  • Link oneTBB with the project’s source code.

  • +
  • Provide the necessary compiler and linker flags.

  • +
+

However, you can use CMake* and the pkg-config tool to simplify the process of integrating oneTBB into your project and handling its dependencies. +See the instructions below to learn how to use the tools.

+
+

CMake*#

+

CMake* is a cross-platform build tool that helps you manage dependencies and build systems. +Integrating oneTBB into your project using CMake*:

+
    +
  • Simplifies the process of building and linking against the library.

  • +
  • Ensures that your project can be built and run on multiple platforms.

  • +
  • Lets you manage oneTBB dependencies.

  • +
+

To add oneTBB to another project using CMake*, add the following commands to your CMakeLists.txt file:

+
find_package(TBB REQUIRED)
+target_link_libraries(my_executable TBB::tbb)
+
+
+

After that, configure your project with CMake* as usual.

+
+
+

Compile a Program Using pkg-config#

+

The pkg-config tool is used to simplify the compilation line by retrieving information about packages +from special metadata files. It helps avoid large hard-coded paths and makes compilation more portable.

+

To compile a test program test.cpp with oneTBB on Linux* OS, +provide the full path to search for included files and libraries, or provide a line as the following:

+
g++ -o test test.cpp $(pkg-config --libs --cflags tbb)
+
+
+

Where:

+

--cflags provides oneTBB library include path:

+
$ pkg-config --cflags tbb
+-I<path-to>/tbb/latest/lib/pkgconfig/../..//include
+
+
+

--libs provides the Intel(R) oneTBB library name and the search path to find it:

+
$ pkg-config libs tbb
+-L<path to>tbb/latest/lib/pkgconfig/../..//lib/intel64/gcc4.8 -ltbb
+
+
+
+

Note

+

For Windows* OS, additionally, use the --msvc-syntax option flag that converts the compiling and linking flags in an appropriate mode.

+
+
+
+ + +
+ + + + + + + + +
+ + + + +
+ + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/GSG/next_steps.html b/GSG/next_steps.html new file mode 100644 index 0000000000..1389f9ed72 --- /dev/null +++ b/GSG/next_steps.html @@ -0,0 +1,817 @@ + + + + + + + + + + + Next Steps — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + + + + + + +
+ +
+

Next Steps#

+

After installing oneTBB, complete the following steps to start working with the library.

+
+

Set the Environment Variables#

+

After installing oneTBB, set the environment variables:

+
    +
  1. Go to the oneTBB installation directory.

  2. +
  3. Set the environment variables using the script in <install_dir> by running:

    +
      +
    • On Linux* OS: vars.{sh|csh} in <install_dir>/tbb/latest/env

    • +
    • On Windows* OS: vars.bat in <install_dir>/tbb/latest/env

    • +
    +
  4. +
+
+

Tip

+

oneTBB can coordinate with Intel(R) OpenMP on CPU resources usage +to avoid excessive oversubscription when both runtimes are used within a process. +To enable this feature set up TCM_ENABLE environment variable to 1.

+
+
+
+

Build and Run a Sample#

+
+
    +
  1. Create a new C++ project using your IDE. In this example, Microsoft* Visual Studio* Code is used.

  2. +
  3. Create an example.cpp file in the project.

  4. +
  5. Copy and paste the code below. It is a typical example of a oneTBB algorithm. The sample calculates a sum of all integer numbers from 1 to 100.

    +
    #include <oneapi/tbb.h>
    +
    +int main (){
    +    int sum = oneapi::tbb::parallel_reduce(
    +        oneapi::tbb::blocked_range<int>(1,101), 0,
    +        [](oneapi::tbb::blocked_range<int> const& r, int init) -> int {
    +            for (int v = r.begin(); v != r.end(); v++) {
    +                init += v;
    +            }
    +            return init;
    +        },
    +        [](int lhs, int rhs) -> int {
    +            return lhs + rhs;
    +        }
    +    );
    +
    +    printf("Sum: %d\n", sum);
    +    return 0;
    +}
    +
    +
    +
  6. +
  7. Open the tasks.json file in the .vscode directory and paste the following lines to the args array:

    +
      +
    • -Ipath/to/oneTBB/include to add oneTBB include directory.

    • +
    • path/to/oneTBB/ to add oneTBB.

    • +
    +

    For example:

    +
    {
    +   "tasks": [
    +        {
    +           "label": "build & run",
    +           "type": "cppbuild",
    +           "group": {
    +           "args": [
    +               "/IC:\\Program Files (x86)\\Intel\\oneAPI\\tbb\\2021.9.0\\include",
    +               "C:\\Program Files (x86)\\Intel\\oneAPI\\tbb\\2021.9.0\\lib\\ia32\\vc14\\tbb12.lib"
    +
    +
    +
  8. +
  9. Build the project.

  10. +
  11. Run the example.

  12. +
  13. If oneTBB is configured correctly, the output displays Sum: 5050.

  14. +
+
+
+
+

Hybrid CPU and NUMA Support#

+

If you need NUMA/Hybrid CPU support in oneTBB, you need to make sure that HWLOC* is installed on your system.

+

HWLOC* (Hardware Locality) is a library that provides a portable abstraction of the hierarchical topology of modern architectures (NUMA, hybrid CPU systems, etc). oneTBB relies on HWLOC* to identify the underlying topology of the system to optimize thread scheduling and memory allocation.

+

Without HWLOC*, oneTBB may not take advantage of NUMA/Hybrid CPU support. Therefore, it’s important to make sure that HWLOC* is installed before using oneTBB on such systems.

+
+

Check HWLOC* on the System#

+

To check if HWLOC* is already installed on your system, run hwloc-ls:

+
    +
  • For Linux* OS, in the command line.

  • +
  • For Windows* OS, in the command prompt.

  • +
+

If HWLOC* is installed, the command displays information about the hardware topology of your system. If it is not installed, you receive an error message saying that the command hwloc-ls could not be found.

+
+

Note

+

For Hybrid CPU support, make sure that HWLOC* is version 2.5 or higher. For NUMA support, install HWLOC* version 1.11 or higher.

+
+
+
+

Install HWLOC*#

+

To install HWLOC*, visit the official Portable Hardware Locality website (https://www-lb.open-mpi.org/projects/hwloc/).

+
    +
  • For Windows* OS, binaries are available for download.

  • +
  • For Linux* OS, only the source code is provided and binaries should be built.

  • +
+

On Linux* OS, HWLOC* can be also installed with package managers, such as APT*, YUM*, etc. To do so, run: sudo apt install hwloc.

+
+

Note

+

For Hybrid CPU support, make sure that HWLOC* is version 2.5 or higher. For NUMA support, install HWLOC* version 1.11 or higher.

+
+
+
+
+ + +
+ + + + + + + + +
+ + + + + + + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/GSG/samples.html b/GSG/samples.html new file mode 100644 index 0000000000..fdfc165dfd --- /dev/null +++ b/GSG/samples.html @@ -0,0 +1,694 @@ + + + + + + + + + + + oneTBB Samples — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

oneTBB Samples

+ +
+
+ +
+
+
+ + + + +
+ +
+

oneTBB Samples#

+

To become an expert in using oneTBB, explore its samples and examples to learn how +to properly utilize the features and functionality of oneTBB and avoid common mistakes that may impede your performance.

+

The following samples are available:

+ +
+

Note

+

You can also refer to the oneAPI Samples to learn more about the ecosystem.

+
+
+ + +
+ + + + + + + + +
+ + + + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/GSG/system_requirements.html b/GSG/system_requirements.html new file mode 100644 index 0000000000..cf090496b5 --- /dev/null +++ b/GSG/system_requirements.html @@ -0,0 +1,636 @@ + + + + + + + + + + + System Requirements — oneTBB documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+

System Requirements

+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+ + + + +
+ + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_images/dependence_graph.jpg b/_images/dependence_graph.jpg new file mode 100644 index 0000000000..d9e4778664 Binary files /dev/null and b/_images/dependence_graph.jpg differ diff --git a/_images/execution_timeline2node.jpg b/_images/execution_timeline2node.jpg new file mode 100644 index 0000000000..f9dae106fa Binary files /dev/null and b/_images/execution_timeline2node.jpg differ diff --git a/_images/execution_timeline_dependence.jpg b/_images/execution_timeline_dependence.jpg new file mode 100644 index 0000000000..119a064d44 Binary files /dev/null and b/_images/execution_timeline_dependence.jpg differ diff --git a/_images/fg_api_graph_structure.png b/_images/fg_api_graph_structure.png new file mode 100644 index 0000000000..05f29c5145 Binary files /dev/null and b/_images/fg_api_graph_structure.png differ diff --git a/_images/flow_graph.jpg b/_images/flow_graph.jpg new file mode 100644 index 0000000000..0085afec91 Binary files /dev/null and b/_images/flow_graph.jpg differ diff --git a/_images/flow_graph_complex.jpg b/_images/flow_graph_complex.jpg new file mode 100644 index 0000000000..09b0fa1ff1 Binary files /dev/null and b/_images/flow_graph_complex.jpg differ diff --git a/_images/flow_graph_message_passing_protocol.jpg b/_images/flow_graph_message_passing_protocol.jpg new file mode 100644 index 0000000000..e132912024 Binary files /dev/null and b/_images/flow_graph_message_passing_protocol.jpg differ diff --git a/_images/flow_graph_reserve_buffers_1.png b/_images/flow_graph_reserve_buffers_1.png new file mode 100644 index 0000000000..e3a00a1dfe Binary files /dev/null and b/_images/flow_graph_reserve_buffers_1.png differ diff --git a/_images/flow_graph_reserve_buffers_2.png b/_images/flow_graph_reserve_buffers_2.png new file mode 100644 index 0000000000..16b3e65112 Binary files /dev/null and b/_images/flow_graph_reserve_buffers_2.png differ diff --git a/_images/flow_graph_reserve_buffers_3.png b/_images/flow_graph_reserve_buffers_3.png new file mode 100644 index 0000000000..213d1bdbaa Binary files /dev/null and b/_images/flow_graph_reserve_buffers_3.png differ diff --git a/_images/flow_graph_reserve_buffers_4.png b/_images/flow_graph_reserve_buffers_4.png new file mode 100644 index 0000000000..e7fd128207 Binary files /dev/null and b/_images/flow_graph_reserve_buffers_4.png differ diff --git a/_images/flow_graph_reserve_buffers_5.png b/_images/flow_graph_reserve_buffers_5.png new file mode 100644 index 0000000000..e501da2c8e Binary files /dev/null and b/_images/flow_graph_reserve_buffers_5.png differ diff --git a/_images/flow_graph_reserve_buffers_6.png b/_images/flow_graph_reserve_buffers_6.png new file mode 100644 index 0000000000..545fbf2e77 Binary files /dev/null and b/_images/flow_graph_reserve_buffers_6.png differ diff --git a/_images/flow_graph_reserve_buffers_7.png b/_images/flow_graph_reserve_buffers_7.png new file mode 100644 index 0000000000..f8a3f3a424 Binary files /dev/null and b/_images/flow_graph_reserve_buffers_7.png differ diff --git a/_images/how-oneTBB-works.png b/_images/how-oneTBB-works.png new file mode 100644 index 0000000000..f72427a814 Binary files /dev/null and b/_images/how-oneTBB-works.png differ diff --git a/_images/image002.jpg b/_images/image002.jpg new file mode 100644 index 0000000000..5e06ac8e79 Binary files /dev/null and b/_images/image002.jpg differ diff --git a/_images/image002a.jpg b/_images/image002a.jpg new file mode 100644 index 0000000000..63ef8c6ddf Binary files /dev/null and b/_images/image002a.jpg differ diff --git a/_images/image003a.jpg b/_images/image003a.jpg new file mode 100644 index 0000000000..30feff069d Binary files /dev/null and b/_images/image003a.jpg differ diff --git a/_images/image004.jpg b/_images/image004.jpg new file mode 100644 index 0000000000..6784605164 Binary files /dev/null and b/_images/image004.jpg differ diff --git a/_images/image004a.jpg b/_images/image004a.jpg new file mode 100644 index 0000000000..144e1e42a9 Binary files /dev/null and b/_images/image004a.jpg differ diff --git a/_images/image005a.jpg b/_images/image005a.jpg new file mode 100644 index 0000000000..0f0953fc91 Binary files /dev/null and b/_images/image005a.jpg differ diff --git a/_images/image006.jpg b/_images/image006.jpg new file mode 100644 index 0000000000..23f2393c87 Binary files /dev/null and b/_images/image006.jpg differ diff --git a/_images/image006a.jpg b/_images/image006a.jpg new file mode 100644 index 0000000000..d1008e4b4b Binary files /dev/null and b/_images/image006a.jpg differ diff --git a/_images/image007.jpg b/_images/image007.jpg new file mode 100644 index 0000000000..6f90d9395a Binary files /dev/null and b/_images/image007.jpg differ diff --git a/_images/image007a.jpg b/_images/image007a.jpg new file mode 100644 index 0000000000..80e7ffccf4 Binary files /dev/null and b/_images/image007a.jpg differ diff --git a/_images/image008.jpg b/_images/image008.jpg new file mode 100644 index 0000000000..e5a2a4bd34 Binary files /dev/null and b/_images/image008.jpg differ diff --git a/_images/image008a.jpg b/_images/image008a.jpg new file mode 100644 index 0000000000..f1199dbaef Binary files /dev/null and b/_images/image008a.jpg differ diff --git a/_images/image009.jpg b/_images/image009.jpg new file mode 100644 index 0000000000..d88d94f26b Binary files /dev/null and b/_images/image009.jpg differ diff --git a/_images/image009a.jpg b/_images/image009a.jpg new file mode 100644 index 0000000000..02cd196067 Binary files /dev/null and b/_images/image009a.jpg differ diff --git a/_images/image010.jpg b/_images/image010.jpg new file mode 100644 index 0000000000..c84ab1d567 Binary files /dev/null and b/_images/image010.jpg differ diff --git a/_images/image011.jpg b/_images/image011.jpg new file mode 100644 index 0000000000..2297c1cc66 Binary files /dev/null and b/_images/image011.jpg differ diff --git a/_images/image012.jpg b/_images/image012.jpg new file mode 100644 index 0000000000..e9b011c85a Binary files /dev/null and b/_images/image012.jpg differ diff --git a/_images/image013.jpg b/_images/image013.jpg new file mode 100644 index 0000000000..838f39e1dd Binary files /dev/null and b/_images/image013.jpg differ diff --git a/_images/make_edges_example.png b/_images/make_edges_example.png new file mode 100644 index 0000000000..8d122e73ff Binary files /dev/null and b/_images/make_edges_example.png differ diff --git a/_images/make_edges_usage.png b/_images/make_edges_usage.png new file mode 100644 index 0000000000..3f9d23c9d5 Binary files /dev/null and b/_images/make_edges_usage.png differ diff --git a/_sources/GSG/get_started.rst b/_sources/GSG/get_started.rst new file mode 100644 index 0000000000..2af04be6b0 --- /dev/null +++ b/_sources/GSG/get_started.rst @@ -0,0 +1,49 @@ +.. _Get_Started_Guide: + +Get Started with |full_name| +============================= + +|short_name| Get Started Guide provides the information you need to begin working with oneTBB. +It is helpful for new users of parallel programming and experienced developers that want to improve code performance. + +It is recommended for you to have a basic knowledge of C++ programming and some experience with parallel programming concepts. + +|full_name| is a runtime-based parallel programming model for C++ code that uses tasks. +The template-based runtime library can help you harness the latent performance of multi-core processors. + +oneTBB enables you to simplify parallel programming by breaking computation into parallel running tasks. Within a single process, +parallelism is carried out by mapping tasks to threads. Threads are an operating system mechanism that allows the same or different sets of instructions +to be executed simultaneously. Using threads can make your program work faster and more efficiently. + +Here you can see one of the possible executions of tasks by threads. + +.. figure:: Images/how-oneTBB-works.png + :scale: 70% + :align: center + +Use oneTBB to write scalable applications that: + +* Specify logical parallel structure instead of threads. +* Emphasize data-parallel programming. +* Take advantage of concurrent collections and parallel algorithms. + +oneTBB supports nested parallelism and load balancing. It means that you can use the library without worrying about oversubscribing a system, which happens when more tasks are assigned to a system than it can handle efficiently. + +oneTBB is used in different areas, such as scientific simulations, gaming, data analysis, etc. + +It is available as a stand-alone product and as part of the |base_tk|. + + +To start using oneTBB, follow the next steps: +********************************************* + +#. See the :ref:`System Requirements`. +#. :ref:`Install oneTBB`. +#. Run your program using oneTBB following the :ref:`Next Steps `. +#. Learn how to :ref:`Integrate oneTBB into your project ` using CMake* and pkg-config tool. +#. See :ref:`oneTBB Samples `. + + + + + diff --git a/_sources/GSG/installation.rst b/_sources/GSG/installation.rst new file mode 100644 index 0000000000..d8f1933265 --- /dev/null +++ b/_sources/GSG/installation.rst @@ -0,0 +1,7 @@ +.. _installation: + +Installation +============ + +See the `installation instructions `_ +that will help you to install |short_name| successfully. \ No newline at end of file diff --git a/_sources/GSG/integrate.rst b/_sources/GSG/integrate.rst new file mode 100644 index 0000000000..2b38dba246 --- /dev/null +++ b/_sources/GSG/integrate.rst @@ -0,0 +1,68 @@ +.. _integrate: + +Integrate oneTBB +================ + +If you want to improve the performance and scalability of your application, you can integrate oneTBB into your project. +For example, you may want to integrate oneTBB if your application needs to process large amounts of data in parallel. + +To integrate oneTBB, you need to: + +* Link oneTBB with the project's source code. +* Provide the necessary compiler and linker flags. + +However, you can use CMake* and the pkg-config tool to simplify the process of integrating oneTBB into your project and handling its dependencies. +See the instructions below to learn how to use the tools. + +CMake* +******* + +CMake* is a cross-platform build tool that helps you manage dependencies and build systems. +Integrating oneTBB into your project using CMake*: + +* Simplifies the process of building and linking against the library. +* Ensures that your project can be built and run on multiple platforms. +* Lets you manage oneTBB dependencies. + +To add oneTBB to another project using CMake*, add the following commands to your ``CMakeLists.txt`` file: + +.. code-block:: cmake + + find_package(TBB REQUIRED) + target_link_libraries(my_executable TBB::tbb) + +After that, configure your project with CMake* as usual. + + +Compile a Program Using pkg-config +*********************************** + +The pkg-config tool is used to simplify the compilation line by retrieving information about packages +from special metadata files. It helps avoid large hard-coded paths and makes compilation more portable. + +To compile a test program ``test.cpp`` with oneTBB on Linux* OS, +provide the full path to search for included files and libraries, or provide a line as the following: + +.. code-block:: + + g++ -o test test.cpp $(pkg-config --libs --cflags tbb) + +Where: + +``--cflags`` provides oneTBB library include path: + +.. code-block:: + + $ pkg-config --cflags tbb + -I/tbb/latest/lib/pkgconfig/../..//include + +``--libs`` provides the Intel(R) oneTBB library name and the search path to find it: + +.. code-block:: + + $ pkg-config –libs tbb + -Ltbb/latest/lib/pkgconfig/../..//lib/intel64/gcc4.8 -ltbb + +.. note:: + + For Windows* OS, additionally, use the ``--msvc-syntax`` option flag that converts the compiling and linking flags in an appropriate mode. diff --git a/_sources/GSG/next_steps.rst b/_sources/GSG/next_steps.rst new file mode 100644 index 0000000000..aeb4407b10 --- /dev/null +++ b/_sources/GSG/next_steps.rst @@ -0,0 +1,157 @@ +.. _next_steps: + +Next Steps +=========== + +After installing oneTBB, complete the following steps to start working with the library. + +Set the Environment Variables +***************************** + +After installing |short_name|, set the environment variables: + +#. Go to the oneTBB installation directory. + +#. Set the environment variables using the script in ```` by running: + + * On Linux* OS: ``vars.{sh|csh} in /tbb/latest/env`` + * On Windows* OS: ``vars.bat in /tbb/latest/env`` + +.. tip:: + + oneTBB can coordinate with Intel(R) OpenMP on CPU resources usage + to avoid excessive oversubscription when both runtimes are used within a process. + To enable this feature set up ``TCM_ENABLE`` environment variable to ``1``. + + +Build and Run a Sample +********************** + +.. tabs:: + + .. group-tab:: Windows* OS + + #. Create a new C++ project using your IDE. In this example, Microsoft* Visual Studio* Code is used. + #. Create an ``example.cpp`` file in the project. + #. Copy and paste the code below. It is a typical example of a |short_name| algorithm. The sample calculates a sum of all integer numbers from 1 to 100. + + .. code:: + + #include + + int main (){ + int sum = oneapi::tbb::parallel_reduce( + oneapi::tbb::blocked_range(1,101), 0, + [](oneapi::tbb::blocked_range const& r, int init) -> int { + for (int v = r.begin(); v != r.end(); v++) { + init += v; + } + return init; + }, + [](int lhs, int rhs) -> int { + return lhs + rhs; + } + ); + + printf("Sum: %d\n", sum); + return 0; + } + + #. Open the ``tasks.json`` file in the ``.vscode`` directory and paste the following lines to the args array: + + * ``-Ipath/to/oneTBB/include`` to add oneTBB include directory. + * ``path/to/oneTBB/`` to add oneTBB. + + For example: + + .. code-block:: + + { + "tasks": [ + { + "label": "build & run", + "type": "cppbuild", + "group": { + "args": [ + "/IC:\\Program Files (x86)\\Intel\\oneAPI\\tbb\\2021.9.0\\include", + "C:\\Program Files (x86)\\Intel\\oneAPI\\tbb\\2021.9.0\\lib\\ia32\\vc14\\tbb12.lib" + + + #. Build the project. + #. Run the example. + #. If oneTBB is configured correctly, the output displays ``Sum: 5050``. + + .. group-tab:: Linux* OS + + #. Create an ``example.cpp`` file in the project. + #. Copy and paste the code below. It is a typical example of a |short_name| algorithm. The sample calculates a sum of all integer numbers from 1 to 100. + + .. code:: + + #include + + int main(){ + int sum = oneapi::tbb::parallel_reduce( + oneapi::tbb::blocked_range(1,101), 0, + [](oneapi::tbb::blocked_range const& r, int init) -> int { + for (int v = r.begin(); v != r.end(); v++) { + init += v; + } + return init; + }, + [](int lhs, int rhs) -> int { + return lhs + rhs; + } + ); + + printf("Sum: %d\n", sum); + return 0; + } + + #. Compile the code using oneTBB. For example, + + .. code-block:: + + g++ -std=c++11 example.cpp -o example -ltbb + + + #. Run the executable: + + .. code-block:: + + ./example + + #. If oneTBB is configured correctly, the output displays ``Sum: 5050``. + + +Hybrid CPU and NUMA Support +**************************** + +If you need NUMA/Hybrid CPU support in oneTBB, you need to make sure that HWLOC* is installed on your system. + +HWLOC* (Hardware Locality) is a library that provides a portable abstraction of the hierarchical topology of modern architectures (NUMA, hybrid CPU systems, etc). oneTBB relies on HWLOC* to identify the underlying topology of the system to optimize thread scheduling and memory allocation. + +Without HWLOC*, oneTBB may not take advantage of NUMA/Hybrid CPU support. Therefore, it's important to make sure that HWLOC* is installed before using oneTBB on such systems. + +Check HWLOC* on the System +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To check if HWLOC* is already installed on your system, run ``hwloc-ls``: + +* For Linux* OS, in the command line. +* For Windows* OS, in the command prompt. + +If HWLOC* is installed, the command displays information about the hardware topology of your system. If it is not installed, you receive an error message saying that the command ``hwloc-ls`` could not be found. + +.. note:: For Hybrid CPU support, make sure that HWLOC* is version 2.5 or higher. For NUMA support, install HWLOC* version 1.11 or higher. + +Install HWLOC* +^^^^^^^^^^^^^^ + +To install HWLOC*, visit the official Portable Hardware Locality website (https://www-lb.open-mpi.org/projects/hwloc/). + +* For Windows* OS, binaries are available for download. +* For Linux* OS, only the source code is provided and binaries should be built. + +On Linux* OS, HWLOC* can be also installed with package managers, such as APT*, YUM*, etc. To do so, run: sudo apt install hwloc. + +.. note:: For Hybrid CPU support, make sure that HWLOC* is version 2.5 or higher. For NUMA support, install HWLOC* version 1.11 or higher. diff --git a/_sources/GSG/samples.rst b/_sources/GSG/samples.rst new file mode 100644 index 0000000000..18bf812801 --- /dev/null +++ b/_sources/GSG/samples.rst @@ -0,0 +1,49 @@ +.. _samples: + +oneTBB Samples +============== + +To become an expert in using oneTBB, explore its samples and examples to learn how +to properly utilize the features and functionality of oneTBB and avoid common mistakes that may impede your performance. + +The following samples are available: + +* **Containers** + + * `concurrent_hash_map `_ + * `concurrent_priority_queue `_ + +* `Flow Graph `_ + * `A solution to the binpacking problem using a queue_node, a buffer_node, and function_node. `_ + * `Cholesky Factorization algorithm `_ + * `An implementation of dining philosophers in graph using the reserving join_node `_ + * `A parallel implementation of bzip2 block-sorting file compressor `_ + * `An example of a collection of digital logic gates that can be easily composed into larger circuits `_ + * `An example of a Kohonen Self-Organizing Map using cancellation `_ + * `Split computational kernel for execution between CPU and GPU `_ + +* **Algorithms** + + * `parallel_for `_ + * `Game of life overlay `_ + * `Polygon overlay `_ + * `Parallel seismic wave simulation `_ + * `Parallel 2-D raytracer/renderer `_ + * `Find largest matching substrings `_ + * `Resumable task: Split computational kernel for execution between CPU and GPU `_ + * `parallel_for_each `_ + * `parallel_pipeline `_ + * `parallel_reduce `_ + +* **Task Scheduler** + + * `task_arena `_ + * `task_group `_ + * `Execute similar computational kernels, with one task executing the SYCL* code and the other task executing the oneTBB code `_ + +* **Other** + + * `Compute Fibonacci numbers in different ways `_ + + +.. note:: You can also refer to the `oneAPI Samples `_ to learn more about the ecosystem. \ No newline at end of file diff --git a/_sources/GSG/system_requirements.rst b/_sources/GSG/system_requirements.rst new file mode 100644 index 0000000000..593680147f --- /dev/null +++ b/_sources/GSG/system_requirements.rst @@ -0,0 +1,6 @@ +.. _System_Requirements: + +System Requirements +******************* + +Refer to the `oneTBB System Requirements `_. \ No newline at end of file diff --git a/_sources/index.rst b/_sources/index.rst new file mode 100644 index 0000000000..e49c5c1c47 --- /dev/null +++ b/_sources/index.rst @@ -0,0 +1,15 @@ +|full_name| +=========== + +.. include:: index/index_intro.rst + + +The following are some important topics for the ``novice user``: + +* :ref:`Get_Started_Guide` gives you a brief explanation of what oneTBB is. +* :ref:`Benefits` describes how |short_name| differs from typical threading packages. +* :ref:`Package_Contents` describes dynamic library files and header files for Windows*, Linux*, and macOS* operating systems used in |short_name|. + +.. include:: index/useful_topics.rst + +.. include:: index/toctree.rst diff --git a/_sources/index/index_intro.rst b/_sources/index/index_intro.rst new file mode 100644 index 0000000000..72055fe8d3 --- /dev/null +++ b/_sources/index/index_intro.rst @@ -0,0 +1,5 @@ +.. _index_intro: + +This document contains information about |short_name|. +It is a flexible performance library that let you break computation into parallel running tasks. + diff --git a/_sources/index/toctree.rst b/_sources/index/toctree.rst new file mode 100644 index 0000000000..542a4bb601 --- /dev/null +++ b/_sources/index/toctree.rst @@ -0,0 +1,38 @@ +.. _toctree: + +.. toctree:: + :caption: About + :maxdepth: 1 + + /main/intro/help_support + /main/intro/notation + /main/intro/intro_os + /main/intro/Benefits + /main/intro/testing_approach + /main/intro/limitations.rst + + +.. toctree:: + :caption: Get Started + :maxdepth: 2 + + /GSG/get_started + /GSG/system_requirements + /GSG/installation + /GSG/next_steps + /GSG/integrate + /GSG/samples + + +.. toctree:: + :maxdepth: 3 + :caption: Developer Guide + + /main/tbb_userguide/title + + +.. toctree:: + :maxdepth: 3 + :caption: Developer Reference + + /main/reference/reference diff --git a/_sources/index/useful_topics.rst b/_sources/index/useful_topics.rst new file mode 100644 index 0000000000..8def9339a6 --- /dev/null +++ b/_sources/index/useful_topics.rst @@ -0,0 +1,5 @@ +.. _Usefull_Topics: + +The following is an important topic for the ``experienced user``: + +:ref:`Migration_Guide` describes how to migrate from TBB to oneTBB. \ No newline at end of file diff --git a/_sources/main/intro/Benefits.rst b/_sources/main/intro/Benefits.rst new file mode 100644 index 0000000000..5058cc71e5 --- /dev/null +++ b/_sources/main/intro/Benefits.rst @@ -0,0 +1,88 @@ +.. _Benefits: + +|short_name| Benefits +===================== + + +|full_name| is a library that helps you leverage multi-core performance +without having to be a threading expert. Typically you can improve +performance for multi-core processors by implementing the key points +explained in the early sections of the Developer Guide. As your +expertise grows, you may want to dive into more complex subjects that +are covered in advanced sections. + + +There are a variety of approaches to parallel programming, ranging from +using platform-dependent threading primitives to exotic new languages. +The advantage of oneTBB is that it works at a higher level than raw +threads, yet does not require exotic languages or compilers. You can use +it with any compiler supporting ISO C++. The library differs from +typical threading packages in the following ways: + + +- **oneTBB enables you to specify logical parallelism instead of + threads**. Most threading packages require you to specify threads. + Programming directly in terms of threads can be tedious and lead to + inefficient programs, because threads are low-level, heavy constructs + that are close to the hardware. Direct programming with threads + forces you to efficiently map logical tasks onto threads. In + contrast, the oneTBB run-time library automatically maps logical + parallelism onto threads in a way that makes efficient use of + processor resources. + + +- **oneTBB targets threading for performance**. Most general-purpose + threading packages support many different kinds of threading, such as + threading for asynchronous events in graphical user interfaces. As a + result, general-purpose packages tend to be low-level tools that + provide a foundation, not a solution. Instead, oneTBB focuses on the + particular goal of parallelizing computationally intensive work, + delivering higher-level, simpler solutions. + + +- **oneTBB is compatible with other threading packages.** Because the + library is not designed to address all threading problems, it can + coexist seamlessly with other threading packages. + + +- **oneTBB emphasizes scalable, data parallel programming**. Breaking a + program up into separate functional blocks, and assigning a separate + thread to each block is a solution that typically does not scale well + since typically the number of functional blocks is fixed. In + contrast, oneTBB emphasizes *data-parallel* programming, enabling + multiple threads to work on different parts of a collection. + Data-parallel programming scales well to larger numbers of processors + by dividing the collection into smaller pieces. With data-parallel + programming, program performance increases as you add processors. + + +- **oneTBB relies on generic programming**. Traditional libraries + specify interfaces in terms of specific types or base classes. + Instead, oneAPI Threading Building Blocks uses generic programming. + The essence of generic programming is writing the best possible + algorithms with the fewest constraints. The C++ Standard Template + Library (STL) is a good example of generic programming in which the + interfaces are specified by *requirements* on types. For example, C++ + STL has a template function ``sort`` that sorts a sequence abstractly + defined in terms of iterators on the sequence. The requirements on + the iterators are: + + + - Provide random access + + + - The expression ``*i<*j`` is true if the item pointed to by + iterator ``i`` should precede the item pointed to by iterator + ``j``, and false otherwise. + + + - The expression ``swap(*i,*j)`` swaps two elements. + + +Specification in terms of requirements on types enables the template to +sort many different representations of sequences, such as vectors and +deques. Similarly, the oneTBB templates specify requirements on types, +not particular types, and thus adapt to different data representations. +Generic programming enables oneTBB to deliver high performance +algorithms with broad applicability. + diff --git a/_sources/main/intro/help_support.rst b/_sources/main/intro/help_support.rst new file mode 100644 index 0000000000..a1ab4097bc --- /dev/null +++ b/_sources/main/intro/help_support.rst @@ -0,0 +1,15 @@ +.. _help_support: + +Getting Help and Support +======================== + + +.. container:: section + + + .. rubric:: Getting Technical Support + :class: sectiontitle + + For general information about oneTBB technical support, product + updates, user forums, FAQs, tips and tricks and other support + questions, go to `GitHub issues `_. diff --git a/_sources/main/intro/intro_os.rst b/_sources/main/intro/intro_os.rst new file mode 100644 index 0000000000..b174120036 --- /dev/null +++ b/_sources/main/intro/intro_os.rst @@ -0,0 +1,35 @@ +.. _intro: + +Introduction +============ + + +|full_name| is a library that supports scalable parallel programming using +standard ISO C++ code. It does not require special languages or +compilers. It is designed to promote scalable data parallel programming. +Additionally, it fully supports nested parallelism, so you can build +larger parallel components from smaller parallel components. To use the +library, you specify tasks, not threads, and let the library map tasks +onto threads in an efficient manner. + + +Many of the library interfaces employ generic programming, in which +interfaces are defined by requirements on types and not specific types. +The C++ Standard Template Library (STL) is an example of generic +programming. Generic programming enables oneTBB to be flexible yet +efficient. The generic interfaces enable you to customize components to +your specific needs. + + +.. note:: + |full_name| requires C++11 standard compiler support. + + +The net result is that oneTBB enables you to specify parallelism far +more conveniently than using raw threads, and at the same time can +improve performance. + + + + + diff --git a/_sources/main/intro/limitations.rst b/_sources/main/intro/limitations.rst new file mode 100644 index 0000000000..dde9f772d7 --- /dev/null +++ b/_sources/main/intro/limitations.rst @@ -0,0 +1,46 @@ +.. _limitations: + +Known Limitations +***************** + +This page outlines the known limitations of oneTBB to help you better understand its capabilities. + +Freestanding Compilation Mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Limitation:** oneTBB does not support the freestanding compilation mode. + +**Risk:** Compiling an application that utilizes oneTBB headers using the Intel(R) oneAPI DPC+/C+ Compiler may result in failure on Windows* OS if the ``/Qfreestanding`` compiler option is employed. + +Static Assert +^^^^^^^^^^^^^ + +**Limitation:** A static assert causes the compilation failures in oneTBB headers if the following conditions are satisfied: + + * Compilation is done with Clang 12.0.0 or a more recent version. + * The LLVM standard library is employed, coupled with the use of the ``-ffreestanding`` flag and C++11/14 compiler options. + +**Risk:** The compilation failures. + +Interface Incompatibilities: TBB vs oneTBB +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Limitation:** An application using Parallel STL algorithms in the ``libstdc++`` versions 9 and 10 may fail to compile due to incompatible interface changes between earlier versions of Threading Building Blocks (TBB) and oneAPI Threading Building Blocks (oneTBB). + +**Solution:** Disable support for Parallel STL algorithms by defining ``PSTL_USE_PARALLEL_POLICIES`` (in libstdc++ 9) or ``_GLIBCXX_USE_TBB_PAR_BACKEND`` (in libstdc++ 10) macro to zero before inclusion of the first standard header file in each translation unit. + +Incorrect Installation Location +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Limitation:** On Linux* OS, if oneAPI Threading Building Blocks (oneTBB) or Threading Building Blocks (TBB) are installed in a system folder, such as ``/usr/lib64``, the application may fail to link due to the order in which the linker searches for libraries. + +**Risk:** The issue does not affect the program execution. + +**Solution:** Use the ``-L`` linker option to specify the correct location of oneTBB library. + +``fork()`` Support +^^^^^^^^^^^^^^^^^^^ + +**Limitation:** oneTBB does not support ``fork()``. + +**Solution:** To work-around the issue, consider using ``task_scheduler_handle`` to join oneTBB worker threads before using ``fork()``. diff --git a/_sources/main/intro/notation.rst b/_sources/main/intro/notation.rst new file mode 100644 index 0000000000..2cc036b54e --- /dev/null +++ b/_sources/main/intro/notation.rst @@ -0,0 +1,106 @@ +.. _notation: + +Notational Conventions +====================== + + +The following conventions may be used in this document. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Convention + - Explanation + - Example + * - \ *Italic* + - Used for introducing new terms, denotation of terms, placeholders, or titles of manuals. + - The filename consists of the *basename* and the *extension*. + * - \ ``Monospace`` + - Indicates directory paths and filenames, commands and command line options, function names, methods, classes, data structures in body text, source code. + - \ ``ippsapi.h`` \ ``\alt\include`` Use the okCreateObjs() function to... \ ``printf("hello, world\n");`` + * - [ ] + - Items enclosed in brackets are optional. + - Fa[c] Indicates Fa or Fac. + * - { \| } + - Braces and vertical bars indicate the choice of one item from a selection of two or more items. + - X{K \| W \| P} Indicates XK, XW, or XP. + * - "[" "]" "{" | " }" "|" + - Writing a metacharacter in quotation marks negates the syntactical meaning stated above; | the character is taken as a literal. + - "[" X "]" [ Y ] Denotes the letter X enclosed in brackets, optionally followed by the letter Y. + * - ... + - The ellipsis indicates that the previous item can be repeated several times. + - \ ``filename`` ... Indicates that one or more filenames can be specified. + * - ,... + - The ellipsis preceded by a comma indicates that the previous item can be repeated several times, | separated by commas. + - \ ``word`` ,... Indicates that one or more words can be specified. If more than one word is specified, the words are comma-separated. + + + + +.. container:: section + + + Class members are summarized by informal class declarations that + describe the class as it seems to clients, not how it is actually + implemented. For example, here is an informal declaration of class + ``Foo``: + + + :: + + + class Foo { + public: + int x(); + int y; + ~Foo(); + }; + + + The actual implementation might look like: + + + :: + + + namespace internal { + class FooBase { + protected: + int x(); + }; + + + class Foo_v3: protected FooBase { + private: + int internal_stuff; + public: + using FooBase::x; + int y; + }; + } + + + typedef internal::Foo_v3 Foo; + + + The example shows two cases where the actual implementation departs + from the informal declaration: + + + - ``Foo`` is actually a typedef to ``Foo_v3``. + + + - Method ``x()`` is inherited from a protected base class. + + + - The destructor is an implicit method generated by the compiler. + + + The informal declarations are intended to show you what you need to + know to use the class without the distraction of irrelevant clutter + particular to the implementation. + diff --git a/_sources/main/intro/testing_approach.rst b/_sources/main/intro/testing_approach.rst new file mode 100644 index 0000000000..7914c32573 --- /dev/null +++ b/_sources/main/intro/testing_approach.rst @@ -0,0 +1,31 @@ +.. _testing_approach: + +Testing Approach +================ + +There are four main types of errors/hazards you can encounter in the development of libraries for parallelism: + +* Interface correspondence to specification +* Memory errors +* Data race +* Race conditions and deadlocks + +|short_name| testing approach is designed to provide high coverage of these error types. +All types of errors are covered with unit testing and review. + +Code coverage metrics are tracked to ensure high code coverage with tests. Uncovered branches are analyzed manually. +Memory errors and data races are additionally covered by special tools that include thread and memory sanitizers. + +Race conditions and deadlocks are the most complicated errors. +They are covered by: + +* **Unit tests** that, however, have limited capability to catch such errors +* **Integration tests**. Multiple different functionalities are heavily combined to emulate user use cases that may trigger such errors based on prior knowledge and expertise. +* **Stress testing with different possible combinations**. It ensures that even rarely triggered error conditions are caught by testing. + +.. note:: Every fix is required to be covered by a test to guarantee the detection of such issues in the future. + +Continuous Integration triggers all the tests on each commit. This ensures that: + +* Issues are detected, starting from the early development phase and up to the moment of integration of changes into the library. +* The highest quality of the library is maintained even in such error-prone domains as parallelism. diff --git a/_sources/main/reference/concurrent_lru_cache_cls.rst b/_sources/main/reference/concurrent_lru_cache_cls.rst new file mode 100644 index 0000000000..07b609ba59 --- /dev/null +++ b/_sources/main/reference/concurrent_lru_cache_cls.rst @@ -0,0 +1,163 @@ +.. _concurrent_lru_cache: + +concurrent_lru_cache +==================== + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_CONCURRENT_LRU_CACHE`` macro to 1. + +A Class Template for Least Recently Used cache with concurrent operations. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +A ``concurrent_lru_cache`` container maps keys to values with the ability +to limit the number of stored unused values. For each key, there is at most one item +stored in the container. + +The container permits multiple threads to concurrently retrieve items from it. + +The container tracks which items are in use by returning a proxy +``concurrent_lru_cache::handle`` object that refers to an item instead of its value. +Once there are no ``handle`` objects holding reference to an item, it is considered unused. + +The container stores all the items that are currently in use plus a limited +number of unused items. Excessive unused items are erased according to +least recently used policy. + +When no item is found for a given key, the container calls the user-specified +``value_function_type`` object to construct a value for the key, and stores that value. +The ``value_function_type`` object must be thread-safe. + +API +*** + +Header +------ + +.. code:: cpp + + #include "oneapi/tbb/concurrent_lru_cache.h" + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + template + class concurrent_lru_cache { + public: + using key_type = Key; + using value_type = Value; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + using value_function_type = ValueFunctionType; + + class handle { + public: + handle(); + handle( handle&& other ); + + ~handle(); + + handle& operator=( handle&& other ); + + operator bool() const; + value_type& value(); + }; // class handle + + concurrent_lru_cache( value_function_type f, std::size_t number_of_lru_history_items ); + ~concurrent_lru_cache(); + + handle operator[]( key_type key ); + }; // class concurrent_lru_cache + } // namespace tbb + } // namespace oneapi + +Member Functions +---------------- + +.. cpp:function:: concurrent_lru_cache( value_function_type f, std::size_t number_of_lru_history_items ); + + **Effects**: Constructs an empty cache that can keep up to ``number_of_lru_history_items`` + unused values, with a function object ``f`` for constructing new values. + +------------------------------------------------------- + +.. cpp:function:: ~concurrent_lru_cache(); + + **Effects**: Destroys the ``concurrent_lru_cache``. Calls the destructors of the stored elements and + deallocates the used storage. + +The behavior is undefined in case of concurrent operations with ``*this``. + +------------------------------------------------------- + +.. cpp:function:: handle operator[]( key_type k ); + + **Effects**: Searches the container for an item that corresponds to the given key. + If such an item is not found, the user-specified function object is called to + construct a value that is inserted into the container. + + **Returns**: a ``handle`` object holding reference to the matching value. + +Member Objects +-------------- + +``handle`` class +^^^^^^^^^^^^^^^^ + +**Member Functions** + +.. cpp:function:: handle(); + + **Effects**: Constructs a ``handle`` object that does not refer to any value. + +-------------------------------------------------- + +.. cpp:function:: handle( handle&& other ); + + **Effects**: Transfers the reference to the value stored in ``concurrent_lru_cache`` + from ``other`` to the newly constructed object. Upon completion, + ``other`` no longer refers to any value. + +--------------------------------------------------- + +.. cpp:function:: ~handle(); + + **Effects**: Releases the reference (if it exists) to a value stored in ``concurrent_lru_cache``. + +The behavior is undefined for concurrent operations with ``*this``. + +--------------------------------------------------- + +.. cpp:function:: handle& operator=( handle&& other ); + + **Effects**: Transfers the reference to a value stored in ``concurrent_lru_cache`` from ``other`` + to ``*this``. If existed, the previous reference held by ``*this`` is released. Upon + completion ``other`` no longer refers to any value. + + **Returns**: a reference to ``*this``. + +--------------------------------------------------- + +.. cpp:function:: operator bool() const; + + **Returns**: ``true`` if ``*this`` holds reference to a value, ``false`` otherwise. + +--------------------------------------------------- + +.. cpp:function:: value_type& value(); + + **Returns**: a reference to a ``value_type`` object stored in ``concurrent_lru_cache``. + +The behavior is undefined if ``*this`` does not refer to any value. diff --git a/_sources/main/reference/constructors_for_nodes.rst b/_sources/main/reference/constructors_for_nodes.rst new file mode 100644 index 0000000000..56e32c2896 --- /dev/null +++ b/_sources/main/reference/constructors_for_nodes.rst @@ -0,0 +1,104 @@ +.. _constructors_for_fg_nodes: + +Constructors for Flow Graph nodes +================================= + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +The "Helper Functions for Expressing Graphs" feature adds a set of new constructors +that can be used to construct a node that ``follows`` or ``precedes`` a set of nodes. + +Where possible, the constructors support Class Template Argument Deduction (since C++17). + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Syntax +------ + +.. code:: cpp + + // continue_node + continue_node(follows(...), Body body, Policy = Policy()); + continue_node(precedes(...), Body body, Policy = Policy()); + + continue_node(follows(...), int number_of_predecessors, Body body, Policy = Policy()); + continue_node(precedes(...), int number_of_predecessors, Body body, Policy = Policy()); + + // function_node + function_node(follows(...), std::size_t concurrency, Policy = Policy()); + function_node(precedes(...), std::size_t concurrency, Policy = Policy()); + + // input_node + input_node(precedes(...), body); + + // multifunction_node + multifunction_node(follows(...), std::size_t concurrency, Body body); + multifunction_node(precedes(...), std::size_t concurrency, Body body); + + // async_node + async_node(follows(...), std::size_t concurrency, Body body); + async_node(precedes(...), std::size_t concurrency, Body body); + + // overwrite_node + explicit overwrite_node(follows(...)); + explicit overwrite_node(precedes(...)); + + // write_once_node + explicit write_once_node(follows(...)); + explicit write_once_node(precedes(...)); + + // buffer_node + explicit buffer_node(follows(...)); + explicit buffer_node(precedes(...)); + + // queue_node + explicit queue_node(follows(...)); + explicit queue_node(precedes(...)); + + // priority_queue_node + explicit priority_queue_node(follows(...), const Compare& comp = Compare()); + explicit priority_queue_node(precedes(...), const Compare& compare = Compare()); + + // sequencer_node + sequencer_node(follows(...), const Sequencer& s); + sequencer_node(precedes(...), const Sequencer& s); + + // limiter_node + limiter_node(follows(...), std::size_t threshold); + limiter_node(precedes(...), std::size_t threshold); + + // broadcast_node + explicit broadcast_node(follows(...)); + explicit broadcast_node(precedes(...)); + + // join_node + explicit join_node(follows(...), Policy = Policy()); + explicit join_node(precedes(...), Policy = Policy()); + + // split_node + explicit split_node(follows(...)); + explicit split_node(precedes(...)); + + // indexer_node + indexer_node(follows(...)); + indexer_node(precedes(...)); + +See Also +******** +:ref:`follows_precedes` diff --git a/_sources/main/reference/custom_mutex_chmap.rst b/_sources/main/reference/custom_mutex_chmap.rst new file mode 100644 index 0000000000..acf502e66d --- /dev/null +++ b/_sources/main/reference/custom_mutex_chmap.rst @@ -0,0 +1,158 @@ +.. _custom_mutex_chmap: + +The customizing mutex type for ``concurrent_hash_map`` +====================================================== + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +oneTBB ``concurrnent_hash_map`` class uses reader-writer mutex +to provide thread safety and avoid data races for insert, lookup, and erasure operations. This feature adds an extra template parameter +for ``concurrent_hash_map`` that allows to customize the type of the reader-writer mutex. + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + + template , + typename Allocator = tbb_allocator>, + typename Mutex = spin_rw_mutex> + class concurrent_hash_map { + using mutex_type = Mutex; + }; + + } // namespace tbb + } // namespace oneapi + +Type requirements +----------------- + +The type of the mutex passed as a template argument for ``concurrent_hash_map`` should +meet the requirements of `ReaderWriterMutex `_. +It should also provide the following API: + +.. cpp:function:: bool ReaderWriterMutex::scoped_lock::is_writer() const; + +**Returns**: ``true`` if the ``scoped_lock`` object acquires the mutex as a writer, ``false`` otherwise. + +The behavior is undefined if the ``scoped_lock`` object does not acquire the mutex. + +``oneapi::tbb::spin_rw_mutex``, ``oneapi::tbb::speculative_spin_rw_mutex``, ``oneapi::tbb::queuing_rw_mutex``, ``oneapi::tbb::null_rw_mutex``, +and ``oneapi::tbb::rw_mutex`` meet the requirements above. + +.. rubric:: Example + +The example below demonstrates how to wrap ``std::shared_mutex`` (C++17) to meet the requirements +of `ReaderWriterMutex` and how to customize ``concurrent_hash_map`` to use this mutex. + +.. code:: cpp + + #define TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1 + #include "oneapi/tbb/concurrent_hash_map.h" + #include + + class SharedMutexWrapper { + public: + // ReaderWriterMutex requirements + + static constexpr bool is_rw_mutex = true; + static constexpr bool is_recursive_mutex = false; + static constexpr bool is_fair_mutex = false; + + class scoped_lock { + public: + scoped_lock() : my_mutex_ptr(nullptr), my_writer_flag(false) {} + scoped_lock(SharedMutexWrapper& mutex, bool write = true) + : my_mutex_ptr(&mutex), my_writer_flag(write) + { + if (my_writer_flag) { + my_mutex_ptr->my_mutex.lock(); + } else { + my_mutex_ptr->my_mutex.lock_shared(); + } + } + + ~scoped_lock() { + if (my_mutex_ptr) release(); + } + + void acquire(SharedMutexWrapper& mutex, bool write = true) { + if (my_mutex_ptr) release(); + + my_mutex_ptr = &mutex; + my_writer_flag = write; + + if (my_writer_flag) { + my_mutex_ptr->my_mutex.lock(); + } else { + my_mutex_ptr->my_mutex.lock_shared(); + } + } + + void release() { + if (my_writer_flag) { + my_mutex_ptr->my_mutex.unlock(); + } else { + my_mutex_ptr->my_mutex.unlock_shared(); + } + } + + bool upgrade_to_writer() { + // std::shared_mutex does not have the upgrade/downgrade parallel_for_each_semantics + if (my_writer_flag) return true; // Already a writer + + my_mutex_ptr->my_mutex.unlock_shared(); + my_mutex_ptr->my_mutex.lock(); + return false; // The lock was reacquired + } + + bool downgrade_to_reader() { + if (!my_writer_flag) return true; // Already a reader + + my_mutex_ptr->my_mutex.unlock(); + my_mutex_ptr->my_mutex.lock_shared(); + return false; + } + + bool is_writer() const { + return my_writer_flag; + } + + private: + SharedMutexWrapper* my_mutex_ptr; + bool my_writer_flag; + }; + private: + std::shared_mutex my_mutex; + }; // struct SharedMutexWrapper + + int main() { + using map_type = oneapi::tbb::concurrent_hash_map, + oneapi::tbb::tbb_allocator>, + SharedMutexWrapper>; + + map_type map; // This object will use SharedMutexWrapper for thread safety of insert/find/erase operations + } diff --git a/_sources/main/reference/follows_and_precedes_functions.rst b/_sources/main/reference/follows_and_precedes_functions.rst new file mode 100644 index 0000000000..efeda8ce19 --- /dev/null +++ b/_sources/main/reference/follows_and_precedes_functions.rst @@ -0,0 +1,78 @@ +.. _follows_precedes: + +``follows`` and ``precedes`` function templates +=============================================== + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +The ``follows`` and ``precedes`` helper functions aid in expressing +dependencies between nodes when building oneTBB flow graphs. These helper functions can +only be used while constructing the node. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +The ``follows`` helper function specifies that the node being constructed is +the successor of the set of nodes passed as an argument. + +The ``precedes`` helper function specifies that the node being constructed is +the predecessor of the set of nodes passed as an argument. + +Functions ``follows`` and ``precedes`` are meant to replace the graph argument, which is +passed as the first argument to the constructor of the node. The graph argument for the +node being constructed is obtained either from the specified node set or the sequence of nodes passed +to ``follows`` or ``precedes``. + +If the nodes passed to ``follows`` or ``precedes`` belong to +different graphs, the behavior is undefined. + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Syntax +------ + +.. code:: cpp + + // node_set is an exposition-only name for the type returned from make_node_set function + + template + /*unspecified*/ follows( node_set& set ); + + template + /*unspecified*/ follows( NodeType& node, NodeTypes&... nodes ); + + template + /*unspecified*/ precedes( node_set& set ); + + template + /*unspecified*/ precedes( NodeType& node, NodeTypes&... nodes ); + +Input Parameters +---------------- + +Either a set or a sequence of nodes can be used as arguments for ``follows`` and +``precedes``. The following expressions are equivalent: + +.. code-block:: cpp + :caption: A set of nodes as an input + + auto handlers = make_node_set(n1, n2, n3); + broadcast_node input(precedes(handlers)); + +.. code-block:: cpp + :caption: A sequence of nodes as an input + + broadcast_node input(precedes(n1, n2, n3)); diff --git a/_sources/main/reference/helpers_for_expressing_graphs.rst b/_sources/main/reference/helpers_for_expressing_graphs.rst new file mode 100644 index 0000000000..537fda0f13 --- /dev/null +++ b/_sources/main/reference/helpers_for_expressing_graphs.rst @@ -0,0 +1,118 @@ +.. _helpers_for_expressing_graphs: + +Helper Functions for Expressing Graphs +====================================== + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +Helper functions are intended to make creation of the flow graphs less verbose. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +This feature adds ``make_edges``, ``make_node_set``, +``follows`` and ``precedes`` functions to ``oneapi::tbb::flow`` namespace. +These functions simplify the process of building flow graphs by allowing to gather nodes +into sets and connect them to other nodes in the graph. + +API +*** + +.. toctree:: + :titlesonly: + + constructors_for_nodes + follows_and_precedes_functions + make_node_set_function + make_edges_function + +Example +******* + +Consider the graph depicted below. + +.. figure:: ./Resources/fg_api_graph_structure.png + :align: center + +In the examples below, C++17 Class Template Argument Deduction is used +to avoid template parameter specification where possible. + +**Regular API** + +.. code:: cpp + + #include + + int main() { + using namespace oneapi::tbb::flow; + + graph g; + + broadcast_node input(g); + + function_node doubler(g, unlimited, [](const int& v) { return 2 * v; }); + function_node squarer(g, unlimited, [](const int&) { return v * v; }); + function_node cuber(g, unlimited, [](const int& v) { return v * v * v; }); + + join_node> join(g); + + int sum = 0; + function_node summer(g, serial, [&](const std::tuple& v) { + int sub_sum = std::get<0>(v) + std::get<1>(v) + std::get<2>(v); + sum += sub_sum; + return sub_sum; + }); + + make_edge(input, doubler); + make_edge(input, squarer); + make_edge(input, cuber); + make_edge(doubler, std::get<0>(join.input_ports())); + make_edge(squarer, std::get<1>(join.input_ports())); + make_edge(cuber, std::get<2>(join.input_ports())); + make_edge(join, summer); + + for (int i = 1; i <= 10; ++i) { + input.try_put(i); + } + g.wait_for_all(); + } + +**Preview API** + +.. code:: cpp + + #define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1 + #include + + int main() { + using namespace oneapi::tbb::flow; + + graph g; + + function_node doubler(g, unlimited, [](const int& v) { return 2 * v; }); + function_node squarer(g, unlimited, [](const int&) { return v * v; }); + function_node cuber(g, unlimited, [](const int& v) { return v * v * v; }); + + auto handlers = make_node_set(doubler, squarer, cuber); + + broadcast_node input(precedes(handlers)); + join_node join(follows(handlers)); + + int sum = 0; + function_node summer(follows(join), serial, + [&](const std::tuple& v) { + int sub_sum = std::get<0>(v) + std::get<1>(v) + std::get<2>(v); + sum += sub_sum; + return sub_sum; + }); + + for (int i = 1; i <= 10; ++i) { + input.try_put(i); + } + g.wait_for_all(); + } diff --git a/_sources/main/reference/make_edges_function.rst b/_sources/main/reference/make_edges_function.rst new file mode 100644 index 0000000000..4035463db6 --- /dev/null +++ b/_sources/main/reference/make_edges_function.rst @@ -0,0 +1,81 @@ +.. _make_edges: + +``make_edges`` function template +================================ + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +The ``make_edges`` function template creates edges between a single node +and each node in a set of nodes. + +There are two ways to connect nodes in a set and a single node using +``make_edges``: + +.. figure:: ./Resources/make_edges_usage.png + :align: center + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Syntax +------ + +.. code:: cpp + + // node_set is an exposition-only name for the type returned from make_node_set function + + template + void make_edges(node_set& set, NodeType& node); + + template + void make_edges(NodeType& node, node_set& set); + +Example +------- + +The example implements the graph structure in the picture below. + +.. figure:: ./Resources/make_edges_example.png + :align: center + +.. code:: cpp + + #define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1 + #include + + int main() { + using namespace oneapi::tbb::flow; + + graph g; + broadcast_node input(g); + + function_node doubler(g, unlimited, [](const int& i) { return 2 * i; }); + function_node squarer(g, unlimited, [](const int& i) { return i * i; }); + function_node cuber(g, unlimited, [](const int& i) { return i * i * i; }); + + buffer_node buffer(g); + + auto handlers = make_node_set(doubler, squarer, cuber); + make_edges(input, handlers); + make_edges(handlers, buffer); + + for (int i = 1; i <= 10; ++i) { + input.try_put(i); + } + g.wait_for_all(); + } diff --git a/_sources/main/reference/make_node_set_function.rst b/_sources/main/reference/make_node_set_function.rst new file mode 100644 index 0000000000..bf8eb15b68 --- /dev/null +++ b/_sources/main/reference/make_node_set_function.rst @@ -0,0 +1,42 @@ +.. _make_node_set: + +``make_node_set`` function template +=================================== + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +The ``make_node_set`` function template creates a set of nodes that +can be passed as arguments to ``make_edges``, ``follows`` and ``precedes`` functions. + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Syntax +------ + +.. code:: cpp + + template + /*unspecified*/ make_node_set( Node& node, Nodes&... nodes ); + +See Also +******** + +:ref:`make_edges` + +:ref:`follows_precedes` \ No newline at end of file diff --git a/_sources/main/reference/parallel_for_each_semantics.rst b/_sources/main/reference/parallel_for_each_semantics.rst new file mode 100644 index 0000000000..c007066b3b --- /dev/null +++ b/_sources/main/reference/parallel_for_each_semantics.rst @@ -0,0 +1,66 @@ +.. _parallel_for_each_semantics: + +parallel_for_each Body semantics and requirements +================================================= + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +This page clarifies `ParallelForEachBody `_ +named requirements for ``tbb::parallel_for_each`` algorithm specification. + +.. code:: cpp + + namespace oneapi { + namespace tbb { + + template + void parallel_for_each( InputIterator first, InputIterator last, Body body ); // overload (1) + template + void parallel_for_each( InputIterator first, InputIterator last, Body body, task_group_context& group ); // overload (2) + + template + void parallel_for_each( Container& c, Body body ); // overload (3) + template + void parallel_for_each( Container& c, Body body, task_group_context& group ); // overload (4) + + template + void parallel_for_each( const Container& c, Body body ); // overload (5) + template + void parallel_for_each( const Container& c, Body body, task_group_context& group ); // overload (6) + + } // namespace tbb + } // namespace oneapi + +Terms +----- + +* ``iterator`` determines the type of the iterator passed into ``parallel_for_each`` algorithm (which is ``InputIterator`` for overloads `(1)` and `(2)` + and ``decltype(std::begin(c))`` for overloads `(3) - (6)`) +* ``value_type`` - the type ``typename std::iterator_traits::value_type`` +* ``reference`` - the type ``typename std::iterator_traits::reference``. + +Requirements for different iterator types +----------------------------------------- + +If the ``iterator`` satisfies `Input iterator` named requirements from [input.iterators] ISO C++ Standard section and do not satisfies +`Forward iterator` named requirements from [forward.iterators] ISO C++ Standard section, ``tbb::parallel_for_each`` requires the execution +of the ``body`` with an object of type ``const value_type&`` or ``value_type&&`` to be well-formed. If both forms are well-formed, an overload with +rvalue reference will be preferred. + +.. caution:: + + If the ``Body`` only takes non-const lvalue reference to ``value_type``, named requirements above are violated and the program can be ill-formed. + +If the ``iterator`` satisfies `Forward iterator` named requirements from [forward.iterators] ISO C++ Standard section, ``tbb::parallel_for_each`` requires the execution of the ``body`` +with an object of type ``reference`` to be well-formed. + +Requirements for ``Body`` with ``feeder`` argument +-------------------------------------------------- + +Additional elements submitted into ``tbb::parallel_for_each`` through the ``feeder::add`` passes to the ``Body`` as rvalues and therefore the corresponding +execution of the ``Body`` is required to be well-formed. diff --git a/_sources/main/reference/parallel_sort_ranges_extension.rst b/_sources/main/reference/parallel_sort_ranges_extension.rst new file mode 100644 index 0000000000..cad65b54b0 --- /dev/null +++ b/_sources/main/reference/parallel_sort_ranges_extension.rst @@ -0,0 +1,72 @@ +.. _parallel_sort_ranges_extension: + +parallel_sort ranges interface extension +======================================== + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +|full_name| implementation extends the `oneapi::tbb::parallel_sort specification `_ +with overloads that takes the container by forwarding reference. + + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Syntax +------ + +.. code:: cpp + + namespace oneapi { + namespace tbb { + + template + void parallel_sort( Container&& c ); + template + void parallel_sort( Container&& c, const Compare& comp ); + + } // namespace tbb + } // namespace oneapi + +Functions +--------- + +.. cpp:function:: template void parallel_sort( Container&& c ); + + Equivalent to ``parallel_sort( std::begin(c), std::end(c), comp )``, where `comp` uses `operator<` to determine relative orderings. + +.. cpp:function:: template void parallel_sort( Container&& c, const Compare& comp ); + + Equivalent to ``parallel_sort( std::begin(c), std::end(c), comp )``. + +Example +------- + +This interface may be used for sorting rvalue or constant views: + +.. code:: cpp + + #include + #include // requires C++20 + #include + + std::span get_span() { + static std::array arr = {3, 2, 1}; + return std::span(arr); + } + + int main() { + tbb::parallel_sort(get_span()); + } diff --git a/_sources/main/reference/reference.rst b/_sources/main/reference/reference.rst new file mode 100644 index 0000000000..4c293c02c7 --- /dev/null +++ b/_sources/main/reference/reference.rst @@ -0,0 +1,53 @@ +.. _reference: + +|short_name| API Reference +========================== + +For oneTBB API Reference, refer to `oneAPI Specification `_. The current supported +version of oneAPI Specification is 1.0. + +Specification extensions +************************ + +|full_name| implements the `oneTBB specification `_. +This document provides additional details or restrictions where necessary. +It also describes features that are not included in the oneTBB specification. + +.. toctree:: + :titlesonly: + + parallel_for_each_semantics + parallel_sort_ranges_extension + scalable_memory_pools/malloc_replacement_log + rvalue_reduce + +Preview features +**************** + +A preview feature is a component of oneTBB introduced to receive early feedback from +users. + +The key properties of a preview feature are: + +- It is off by default and must be explicitly enabled. +- It is intended to have a high quality implementation. +- There is no guarantee of future existence or compatibility. +- It may have limited or no support in tools such as correctness analyzers, profilers and debuggers. + + +.. caution:: + A preview feature is subject to change in future. It might be removed or significantly + altered in future releases. Changes to a preview feature do NOT require + usual deprecation and removal process. Therefore, using preview features in production code + is strongly discouraged. + +.. toctree:: + :titlesonly: + + type_specified_message_keys + scalable_memory_pools + helpers_for_expressing_graphs + concurrent_lru_cache_cls + task_group_extensions + custom_mutex_chmap + try_put_and_wait diff --git a/_sources/main/reference/rvalue_reduce.rst b/_sources/main/reference/rvalue_reduce.rst new file mode 100644 index 0000000000..7cf66d86b3 --- /dev/null +++ b/_sources/main/reference/rvalue_reduce.rst @@ -0,0 +1,91 @@ +.. _rvalue_reduce: + +Parallel Reduction for rvalues +============================== + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +|full_name| implementation extends the `ParallelReduceFunc `_ and +`ParallelReduceReduction `_ +to optimize operating with ``rvalues`` using functional form of ``tbb::parallel_reduce`` and ``tbb::parallel_deterministic_reduce`` algorithms. + +API +*** + +Header +------ + +.. code:: cpp + + #include + +ParallelReduceFunc Requirements: Pseudo-Signature, Semantics +------------------------------------------------------------ + +.. cpp:function:: Value Func::operator()(const Range& range, Value&& x) const + +or + +.. cpp:function:: Value Func::operator()(const Range& range, const Value& x) const + + Accumulates the result for a subrange, starting with initial value ``x``. The ``Range`` type must meet the + `Range requirements `_. + The ``Value`` type must be the same as a corresponding template parameter for the `parallel_reduce algorithm `_. + + If both ``rvalue`` and ``lvalue`` forms are provided, the ``rvalue`` is preferred. + +ParallelReduceReduction Requirements: Pseudo-Signature, Semantics +----------------------------------------------------------------- + +.. cpp:function:: Value Reduction::operator()(Value&& x, Value&& y) const + +or + +.. cpp:function:: Value Reduction::operator()(const Value& x, const Value& y) const + + Combines the ``x`` and ``y`` results. The ``Value`` type must be the same as a corresponding template parameter for the `parallel_reduce algorithm `_. + + If both ``rvalue`` and ``lvalue`` forms are provided, the ``rvalue`` is preferred. + +Example +******* + +.. code:: cpp + + // C++17 + #include + #include + #include + #include + + int main() { + std::vector> sets = ...; + + oneapi::tbb::parallel_reduce(oneapi::tbb::blocked_range(0, sets.size()), + std::set{}, // identity element - empty set + [&](const oneapi::tbb::blocked_range& range, std::set&& value) { + for (size_t i = range.begin(); i < range.end(); ++i) { + // Having value as a non-const rvalue reference allows to efficiently + // transfer nodes from sets[i] without copying/moving the data + value.merge(std::move(sets[i])); + } + return value; + }, + [&](std::set&& x, std::set&& y) { + x.merge(std::move(y)); + return x; + } + ); + } + +.. rubric:: See also + +* `oneapi::tbb::parallel_reduce specification `_ +* `oneapi::tbb::parallel_deterministic_reduce specification `_ +* `ParallelReduceFunc specification `_ +* `ParallelReduceReduction specification `_ diff --git a/_sources/main/reference/scalable_memory_pools.rst b/_sources/main/reference/scalable_memory_pools.rst new file mode 100644 index 0000000000..d04cd54a90 --- /dev/null +++ b/_sources/main/reference/scalable_memory_pools.rst @@ -0,0 +1,44 @@ +.. _scalable_memory_pools_reference: + +Scalable Memory Pools +===================== + +.. note:: + To enable this feature, set the ``TBB_PREVIEW_MEMORY_POOL`` macro to 1. + +Memory pools allocate and free memory from a specified region or an underlying allocator using +thread-safe, scalable operations. The following table summarizes the Memory Pool named requirement. +Here, ``P`` represents an instance of the memory pool class. + +.. container:: tablenoborder + + .. list-table:: + :header-rows: 1 + + * - Pseudo-Signature + - Semantics + * - \ ``~P() throw();`` + - Destructor. Frees all the allocated memory. + * - \ ``void P::recycle();`` + - Frees all the allocated memory. + * - \ ``void* P::malloc(size_t n);`` + - Returns a pointer to ``n`` bytes allocated from the memory pool. + * - \ ``void P::free(void* ptr);`` + - Frees the memory object specified via ``ptr`` pointer. + * - \ ``void* P::realloc(void* ptr, size_t n);`` + - Reallocates the memory object pointed by ``ptr`` to ``n`` bytes. + +.. container:: section + + .. rubric:: Model Types + :class: sectiontitle + + The ``memory_pool`` template class and the ``fixed_pool`` class meet the Memory Pool named requirement. + +.. toctree:: + :titlesonly: + + scalable_memory_pools/memory_pool_cls + scalable_memory_pools/fixed_pool_cls + scalable_memory_pools/memory_pool_allocator_cls + diff --git a/_sources/main/reference/scalable_memory_pools/fixed_pool_cls.rst b/_sources/main/reference/scalable_memory_pools/fixed_pool_cls.rst new file mode 100644 index 0000000000..283842572a --- /dev/null +++ b/_sources/main/reference/scalable_memory_pools/fixed_pool_cls.rst @@ -0,0 +1,76 @@ +.. _fixed_pool_cls: + +fixed_pool +========== + +.. note:: + To enable this feature, set the ``TBB_PREVIEW_MEMORY_POOL`` macro to 1. + +A class for scalable memory allocation from a buffer of fixed size. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +``fixed_pool`` allocates and frees memory in a way that scales with the number of processors. +All the memory available for the allocation is initially passed through arguments of the constructor. +``fixed_pool`` meet the :doc:`Memory Pool named requirement<../scalable_memory_pools>`. + +API +*** + +Header +------ + +.. code:: cpp + + #include "oneapi/tbb/memory_pool.h" + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + class fixed_pool { + public: + fixed_pool(void *buffer, size_t size); + fixed_pool(const fixed_pool& other) = delete; + fixed_pool& operator=(const fixed_pool& other) = delete; + ~fixed_pool(); + + void recycle(); + void* malloc(size_t size); + void free(void* ptr); + void* realloc(void* ptr, size_t size); + }; + } // namespace tbb + } // namespace oneapi + +Member Functions +---------------- + +.. cpp:function:: fixed_pool(void *buffer, size_t size) + + **Effects**: Constructs a memory pool to manage the memory of size ``size`` pointed to by ``buffer``. + Throws the ``bad_alloc`` exception if the library fails to construct an instance of the class. + +Examples +******** + +The code below provides a simple example of allocation from a fixed pool. + +.. code:: cpp + + #define TBB_PREVIEW_MEMORY_POOL 1 + #include "oneapi/tbb/memory_pool.h" + ... + char buf[1024*1024]; + oneapi::tbb::fixed_pool my_pool(buf, 1024*1024); + void* my_ptr = my_pool.malloc(10); + my_pool.free(my_ptr);} + diff --git a/_sources/main/reference/scalable_memory_pools/malloc_replacement_log.rst b/_sources/main/reference/scalable_memory_pools/malloc_replacement_log.rst new file mode 100644 index 0000000000..8fea89f949 --- /dev/null +++ b/_sources/main/reference/scalable_memory_pools/malloc_replacement_log.rst @@ -0,0 +1,84 @@ +.. _malloc_replacement_log: + +TBB_malloc_replacement_log Function +=================================== + +.. note:: This function is for Windows* OS only. + +Summary +******* + +Provides information about the status of dynamic memory allocation replacement. + +Syntax +******* + +:: + + extern "C" int TBB_malloc_replacement_log(char *** log_ptr); + + +Header +****** + +:: + + #include "oneapi/tbb/tbbmalloc_proxy.h" + + +Description +*********** + +Dynamic replacement of memory allocation functions on Windows* OS uses in-memory binary instrumentation techniques. +To make sure that such instrumentation is safe, oneTBB first searches for a subset of replaced functions in the Visual C++* runtime DLLs +and checks if each one has a known bytecode pattern. If any required function is not found or its bytecode pattern is unknown, the replacement is skipped, +and the program continues to use the standard memory allocation functions. + +The ``TBB_malloc_replacement_log`` function allows the program to check if the dynamic memory replacement happens and to get a log of the performed checks. + +**Returns:** + +* 0, if all necessary functions are successfully found and the replacement takes place. +* 1, otherwise. + +The ``log_ptr`` parameter must be an address of a char** variable or be ``NULL``. If it is not ``NULL``, the function writes there the address of an array of +NULL-terminated strings containing detailed information about the searched functions in the following format: + +:: + + search_status: function_name (dll_name), byte pattern: + + +For more information about the replacement of dynamic memory allocation functions, see :ref:`Windows_C_Dynamic_Memory_Interface_Replacement`. + + +Example +******* + +:: + + #include "oneapi/tbb/tbbmalloc_proxy.h" + #include + + int main(){ + char **func_replacement_log; + int func_replacement_status = TBB_malloc_replacement_log(&func_replacement_log); + + if (func_replacement_status != 0) { + printf("tbbmalloc_proxy cannot replace memory allocation routines\n"); + for (char** log_string = func_replacement_log; *log_string != 0; log_string++) { + printf("%s\n",*log_string); + } + } + + return 0; + } + + +Example output: + +:: + + tbbmalloc_proxy cannot replace memory allocation routines + Success: free (ucrtbase.dll), byte pattern: + Fail: _msize (ucrtbase.dll), byte pattern: diff --git a/_sources/main/reference/scalable_memory_pools/memory_pool_allocator_cls.rst b/_sources/main/reference/scalable_memory_pools/memory_pool_allocator_cls.rst new file mode 100644 index 0000000000..d275c02f5e --- /dev/null +++ b/_sources/main/reference/scalable_memory_pools/memory_pool_allocator_cls.rst @@ -0,0 +1,117 @@ +.. _memory_pool_allocator_cls: + +memory_pool_allocator +===================== + +.. note:: + To enable this feature, set the ``TBB_PREVIEW_MEMORY_POOL`` macro to 1. + +A class template that provides a memory pool with a C++ allocator interface. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +``memory_pool_allocator`` meets the allocator requirements from the [allocator.requirements] ISO C++ Standard section +It also provides a constructor to allocate and deallocate memory. +This constructor is linked with an instance of either the ``memory_pool`` or the ``fixed_pool`` class. +The class is mainly intended for enabling memory pools within STL containers. + +API +*** + +Header +------ + +.. code:: cpp + + #include "oneapi/tbb/memory_pool.h" + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + template + class memory_pool_allocator { + public: + using value_type = T; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = size_t; + using difference_type = ptrdiff_t; + template struct rebind { + using other = memory_pool_allocator; + }; + explicit memory_pool_allocator(memory_pool &pool) throw(); + explicit memory_pool_allocator(fixed_pool &pool) throw(); + memory_pool_allocator(const memory_pool_allocator& src) throw(); + template + memory_pool_allocator(const memory_pool_allocator& src) throw(); + pointer address(reference x) const; + const_pointer address(const_reference x) const; + pointer allocate(size_type n, const void* hint=0); + void deallocate(pointer p, size_type); + size_type max_size() const throw(); + void construct(pointer p, const T& value); + void destroy(pointer p); + }; + + template<> + class memory_pool_allocator { + public: + using pointer = void*; + using const_pointer = const void*; + using value_type = void; + template struct rebind { + using other = memory_pool_allocator; + }; + memory_pool_allocator(memory_pool &pool) throw(); + memory_pool_allocator(fixed_pool &pool) throw(); + memory_pool_allocator(const memory_pool_allocator& src) throw(); + template + memory_pool_allocator(const memory_pool_allocator& src) throw(); + }; + } // namespace tbb + } // namespace oneapi + + template + inline bool operator==( const memory_pool_allocator& a, + const memory_pool_allocator& b); + template + inline bool operator!=( const memory_pool_allocator& a, + const memory_pool_allocator& b); + +Member Functions +---------------- + +.. cpp:function:: explicit memory_pool_allocator(memory_pool &pool) + + **Effects**: Constructs a memory pool allocator serviced by ``memory_pool`` instance pool. + +------------------------------------------------------- + +.. cpp:function:: explicit memory_pool_allocator(fixed_pool &pool) + + **Effects**: Constructs a memory pool allocator serviced by ``fixed_pool`` instance pool. + +Examples +******** + +The code below provides a simple example of container construction with the use of a memory pool. + +.. code:: cpp + + #define TBB_PREVIEW_MEMORY_POOL 1 + #include "oneapi/tbb/memory_pool.h" + ... + typedef oneapi::tbb::memory_pool_allocator + pool_allocator_t; + std::list my_list(pool_allocator_t( my_pool )); diff --git a/_sources/main/reference/scalable_memory_pools/memory_pool_cls.rst b/_sources/main/reference/scalable_memory_pools/memory_pool_cls.rst new file mode 100644 index 0000000000..fefade1c61 --- /dev/null +++ b/_sources/main/reference/scalable_memory_pools/memory_pool_cls.rst @@ -0,0 +1,80 @@ +.. _memory_pool_cls: + +memory_pool +=========== + +.. note:: + To enable this feature, set the ``TBB_PREVIEW_MEMORY_POOL`` macro to 1. + +A class template for scalable memory allocation from memory blocks provided by an underlying allocator. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +A ``memory_pool`` allocates and frees memory in a way that scales with the number of processors. +The memory is obtained as big chunks from an underlying allocator specified by the template +argument. The latter must satisfy the subset of the allocator requirements from the [allocator.requirements] +ISO C++ Standard section. A ``memory_pool`` meet the :doc:`Memory Pool named requirement<../scalable_memory_pools>`. + +.. caution:: + + If the underlying allocator refers to another scalable memory pool, the inner pool (or pools) + must be destroyed before the outer pool is destroyed or recycled. + +API +*** + +Header +------ + +.. code:: cpp + + #include "oneapi/tbb/memory_pool.h" + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + template + class memory_pool { + public: + explicit memory_pool(const Alloc &src = Alloc()); + memory_pool(const memory_pool& other) = delete; + memory_pool& operator=(const memory_pool& other) = delete; + ~memory_pool(); + void recycle(); + void *malloc(size_t size); + void free(void* ptr); + void *realloc(void* ptr, size_t size); + }; + } + } + +Member Functions +---------------- + +.. cpp:function:: explicit memory_pool(const Alloc &src = Alloc()) + + **Effects**: Constructs a memory pool with an instance of underlying memory allocator of type ``Alloc`` copied from ``src``. + Throws the ``bad_alloc`` exception if runtime fails to construct an instance of the class. + +Examples +******** + +The code below provides a simple example of allocation from an extensible memory pool. + +.. code:: cpp + + #define TBB_PREVIEW_MEMORY_POOL 1 + #include "oneapi/tbb/memory_pool.h" + ... + oneapi::tbb::memory_pool > my_pool; + void* my_ptr = my_pool.malloc(10); + my_pool.free(my_ptr); diff --git a/_sources/main/reference/task_group_extensions.rst b/_sources/main/reference/task_group_extensions.rst new file mode 100644 index 0000000000..47795f9574 --- /dev/null +++ b/_sources/main/reference/task_group_extensions.rst @@ -0,0 +1,89 @@ +.. _task_group_extensions: + +task_group extensions +===================== + +.. note:: + To enable these extensions, set the ``TBB_PREVIEW_TASK_GROUP_EXTENSIONS`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +|full_name| implementation extends the `tbb::task_group specification `_ with the requirements for a user-provided function object. + + +API +*** + +Header +------ + +.. code:: cpp + + #include + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + + class task_group { + public: + + //only the requirements for the return type of function F are changed + template + task_handle defer(F&& f); + + //only the requirements for the return type of function F are changed + template + task_group_status run_and_wait(const F& f); + + //only the requirements for the return type of function F are changed + template + void run(F&& f); + }; + + } // namespace tbb + } // namespace oneapi + + + +Member Functions +---------------- + +.. cpp:function:: template task_handle defer(F&& f) + +As an optimization hint, ``F`` might return a ``task_handle``, which task object can be executed next. + +.. note:: + The ``task_handle`` returned by the function must be created using ``*this`` ``task_group``. That is, the one for which the run method is called, otherwise it is undefined behavior. + +.. cpp:function:: template task_group_status run_and_wait(const F& f) + +As an optimization hint, ``F`` might return a ``task_handle``, which task object can be executed next. + +.. note:: + The ``task_handle`` returned by the function must be created using ``*this`` ``task_group``. That is, the one for which the run method is called, otherwise it is undefined behavior. + + +.. cpp:function:: template void run(F&& f) + +As an optimization hint, ``F`` might return a ``task_handle``, which task object can be executed next. + +.. note:: + The ``task_handle`` returned by the function must be created with ``*this`` ``task_group``. It means, with the one for which run method is called, otherwise it is an undefined behavior. + + +.. rubric:: See also + +* `oneapi::tbb::task_group specification `_ +* `oneapi::tbb::task_group_context specification `_ +* `oneapi::tbb::task_group_status specification `_ +* `oneapi::tbb::task_handle class `_ diff --git a/_sources/main/reference/try_put_and_wait.rst b/_sources/main/reference/try_put_and_wait.rst new file mode 100644 index 0000000000..4e05961f39 --- /dev/null +++ b/_sources/main/reference/try_put_and_wait.rst @@ -0,0 +1,324 @@ +.. _try_put_and_wait: + +Waiting for Single Messages in Flow Graph +========================================= + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +This feature adds a new ``try_put_and_wait`` interface to the receiving nodes in the Flow Graph. +This function puts a message as an input into a Flow Graph and waits until all work related to +that message is complete. +``try_put_and_wait`` may reduce latency compared to calling ``graph::wait_for_all`` since +``graph::wait_for_all`` waits for all work, including work that is unrelated to the input message, to complete. + +``node.try_put_and_wait(msg)`` performs ``node.try_put(msg)`` on the node and waits until the work on ``msg`` is completed. +Therefore, the following conditions are true: + +* Any task initiated by any node in the Flow Graph that involves working with ``msg`` or any other intermediate result + computed from ``msg`` is completed. +* No intermediate results computed from ``msg`` remain in any buffers in the graph. + +.. caution:: + + To prevent ``try_put_and_wait`` calls from infinite waiting, avoid using buffering nodes at the end of the Flow Graph since the final result + will not be automatically consumed by the Flow Graph. + +.. caution:: + + The ``multifunction_node`` and ``async_node`` classes are not currently supported by this feature. Including one of these nodes in the + Flow Graph may cause ``try_put_and_wait`` to exit early, even if the computations on the initial input message are + still in progress. + +API +*** + +Header +------ + +.. code:: cpp + + #define TBB_PREVIEW_FLOW_GRAPH_FEATURES // macro option 1 + #define TBB_PREVIEW_FLOW_GRAPH_TRY_PUT_AND_WAIT // macro option 2 + #include + +Synopsis +-------- + +.. code:: cpp + + namespace oneapi { + namespace tbb { + template + class continue_node { + public: + bool try_put_and_wait(const continue_msg& input); + }; // class continue_node + + template + class function_node { + public: + bool try_put_and_wait(const Input& input); + }; // class function_node + + template + class overwrite_node { + public: + bool try_put_and_wait(const T& input); + }; // class overwrite_node + + template + class write_once_node { + public: + bool try_put_and_wait(const T& input); + }; // class write_once_node + + template + class buffer_node { + public: + bool try_put_and_wait(const T& input); + }; // class buffer_node + + template + class queue_node { + public: + bool try_put_and_wait(const T& input); + }; // class queue_node + + template > + class priority_queue_node { + public: + bool try_put_and_wait(const T& input); + }; // class priority_queue_node + + template + class sequencer_node { + public: + bool try_put_and_wait(const T& input); + }; // class sequencer_node + + template + class limiter_node { + public: + bool try_put_and_wait(const T& input); + }; // class limiter_node + + template + class broadcast_node { + public: + bool try_put_and_wait(const T& input); + }; // class broadcast_node + + template + class split_node { + public: + bool try_put_and_wait(const TupleType& input); + }; // class split_node + } // namespace tbb + } // namespace oneapi + +Member Functions +---------------- + +.. code:: cpp + + template + bool continue_node::try_put_and_wait(const continue_msg& input) + +**Effects**: Increments the count of input signals received. If the incremented count is equal to the number +of known predecessors, performs the ``body`` function object execution. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. code:: cpp + + template + bool function_node::try_put_and_wait(const Input& input) + +**Effects**: If the concurrency limit allows, executes the user-provided body on the incoming message ``input``. +Otherwise, depending on the ``Policy`` of the node, either queues the incoming message ``input`` or rejects it. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true`` if the input is accepted, ``false`` otherwise. + +.. code:: cpp + + template + bool overwrite_node::try_put_and_wait(const T& input) + +**Effects**: Stores ``input`` in the internal single-item buffer and broadcasts it to all successors. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. caution:: + + Since the input element is not retrieved from ``overwrite_node`` once accepted by the successor, + retrieve it by explicitly calling the ``clear()`` method or by overwriting with another element to prevent + ``try_put_and_wait`` from indefinite waiting. + +.. code:: cpp + + template + bool write_once_node::try_put_and_wait(const T& input) + +**Effects**: Stores ``input`` in the internal single-item buffer if it does not contain a valid value already. +If a new value is set, the node broadcasts it to all successors. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true`` for the first time after construction or a call to ``clear()``. + +.. caution:: + + Since the input element is not retrieved from the ``write_once_node`` once accepted by the successor, + retrieve it by explicitly calling the ``clear()`` method to prevent ``try_put_and_wait`` from indefinite waiting. + +.. code:: cpp + + template + bool buffer_node::try_put_and_wait(const T& input) + +**Effects**: Adds ``input`` to the set of items managed by the node and tries forwarding it to a successor. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. code:: cpp + + template + bool queue_node::try_put_and_wait(const T& input) + +**Effects**: Adds ``input`` to the set of items managed by the node and tries forwarding the least recently added item +to a successor. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. code:: cpp + + template + bool priority_queue_node::try_put_and_wait(const T& input) + +**Effects**: Adds ``input`` to the ``priority_queue_node`` and attempts to forward the item with the highest +priority among all items added to the node but not yet forwarded to the successors. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. code:: cpp + + template + bool sequencer_node::try_put_and_wait(const T& input) + +**Effects**: Adds ``input`` to the ``sequencer_node`` and tries forwarding the next item in sequence to a successor. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +.. code:: cpp + + template + bool limiter_node::try_put_and_wait(const T& input) + +**Effects**: If the broadcast count is below the threshold, broadcasts ``input`` to all successors. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true`` if ``input`` is broadcasted; ``false`` otherwise. + +.. code:: cpp + + template + bool broadcast_node::try_put_and_wait(const T& input) + +**Effects**: Broadcasts ``input`` to all successors. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true`` even if the node cannot successfully forward the message to any of its successors. + +.. code:: cpp + + template + bool split_node::try_put_and_wait(const TupleType& input); + +**Effects**: Broadcasts each element in the incoming tuple to the nodes connected to the ``split_node`` output ports. +The element at index ``i`` of ``input`` is broadcasted through the output port number ``i``. + +Waits for the completion of the ``input`` in the Flow Graph, meaning all tasks created by each node and +related to ``input`` are executed, and no related objects remain in any buffer within the graph. + +**Returns**: ``true``. + +Example +******* + +.. code:: cpp + + #define TBB_PREVIEW_FLOW_GRAPH_TRY_PUT_AND_WAIT + #include + #include + + struct f1_body; + struct f2_body; + struct f3_body; + struct f4_body; + + int main() { + using namespace oneapi::tbb; + + flow::graph g; + flow::broadcast_node start_node(g); + + flow::function_node f1(g, flow::unlimited, f1_body{}); + flow::function_node f2(g, flow::unlimited, f2_body{}); + flow::function_node f3(g, flow::unlimited, f3_body{}); + + flow::join_node> join(g); + + flow::function_node, int> f4(g, flow::serial, f4_body{}); + + flow::make_edge(start_node, f1); + flow::make_edge(f1, f2); + + flow::make_edge(start_node, f3); + + flow::make_edge(f2, flow::input_port<0>(join)); + flow::make_edge(f3, flow::input_port<1>(join)); + + flow::make_edge(join, f4); + + // Submit work into the graph + parallel_for(0, 100, [](int input) { + start_node.try_put_and_wait(input); + + // Post processing the result of input + }); + } + +Each iteration of ``parallel_for`` submits an input into the Flow Graph. After returning from ``try_put_and_wait(input)``, it is +guaranteed that all of the work related to the completion of ``input`` is done by all of the nodes in the graph. Tasks related to inputs +submitted by other calls are not guaranteed to be completed. diff --git a/_sources/main/reference/type_specified_message_keys.rst b/_sources/main/reference/type_specified_message_keys.rst new file mode 100644 index 0000000000..a50cd7f434 --- /dev/null +++ b/_sources/main/reference/type_specified_message_keys.rst @@ -0,0 +1,69 @@ +.. _class_join_node_extension: + +Type-specified message keys for join_node +========================================= + +.. note:: + To enable this feature, define the ``TBB_PREVIEW_FLOW_GRAPH_FEATURES`` macro to 1. + +.. contents:: + :local: + :depth: 1 + +Description +*********** + +The extension allows a key matching ``join_node`` to obtain keys via functions associated with +its input types. The extension simplifies the existing approach by removing the need to +provide a function object for each input port of ``join_node``. + +API +*** + +Header +------ + +.. code:: cpp + + #include "oneapi/tbb/flow_graph.h" + +Syntax +------ + +The extension adds a special constructor to the ``join_node`` interface when the +``key_matching`` policy is +used. The constructor has the following signature: + +.. code:: cpp + + join_node( graph &g ) + +When constructed this way, a ``join_node`` calls the +``key_from_message`` function for each incoming message to obtain the key associated +with it. The default implementation of ``key_from_message`` is the following + +.. code:: cpp + + namespace oneapi { + namespace tbb { + namespace flow { + template + K key_from_message( const T &t ) { + return t.key(); + } + } + } + } + +``T`` is one of the user-provided types in ``OutputTuple`` and is +used to construct the ``join_node``, and ``K`` is the key type +of the node. +By default, the ``key()`` method defined in the message class will be called. +Alternatively, the user can define its own ``key_from_message`` function in the +same namespace with the message type. This function will be found via C++ argument-dependent +lookup and used in place of the default implementation. + +See Also +******** + +`join_node Specification `_ diff --git a/_sources/main/tbb_userguide/Advanced_Example.rst b/_sources/main/tbb_userguide/Advanced_Example.rst new file mode 100644 index 0000000000..1277754ea7 --- /dev/null +++ b/_sources/main/tbb_userguide/Advanced_Example.rst @@ -0,0 +1,96 @@ +.. _Advanced_Example: + +Advanced Example +================ + + +An example of a more advanced associative operation is to find the index +where ``Foo(i)`` is minimized. A serial version might look like this: + + +:: + + + long SerialMinIndexFoo( const float a[], size_t n ) { + float value_of_min = FLT_MAX; // FLT_MAX from + long index_of_min = -1; + for( size_t i=0; i& r ) { + const float *a = my_a; + for( size_t i=r.begin(); i!=r.end(); ++i ) { + float value = Foo(a[i]); + if( value + index_of_min(-1) + {} +   + + void join( const SumFoo& y ) { + if( y.value_of_min + index_of_min(-1), + {} + }; + + +Now ``SerialMinIndex`` can be rewritten using ``parallel_reduce`` as +shown below: + + +:: + + + long ParallelMinIndexFoo( float a[], size_t n ) { + MinIndexFoo mif(a); + parallel_reduce(blocked_range(0,n), mif ); + + + return mif.index_of_min; + } diff --git a/_sources/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst b/_sources/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst new file mode 100644 index 0000000000..99446ab659 --- /dev/null +++ b/_sources/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst @@ -0,0 +1,111 @@ +.. _Advanced_Topic_Other_Kinds_of_Iteration_Spaces: + +Advanced Topic: Other Kinds of Iteration Spaces +=============================================== + + +The examples so far have used the class ``blocked_range`` to specify ranges. +This class is useful in many situations, but it does not fit every situation. +You can use |full_name| to define your own iteration space objects. The object +must specify how it can be split into subspaces by providing a basic splitting +constructor, an optional proportional splitting constructor, and two predicate +methods. If your class is called ``R``, the methods and constructors should be +as follows: + + +:: + + + class R { + // True if range is empty + bool empty() const; + // True if range can be split into non-empty subranges + bool is_divisible() const; + // Splits r into subranges r and *this + R( R& r, split ); + // (optional) Splits r into subranges r and *this in proportion p + R( R& r, proportional_split p ); + ... + }; + + + + +The method ``empty`` should return true if the range is empty. The +method ``is_divisible`` should return true if the range can be split +into two non-empty subspaces, and such a split is worth the overhead. +The basic splitting constructor should take two arguments: + + +- The first of type ``R`` + + +- The second of type oneapi::tbb::split + + +The second argument is not used; it serves only to distinguish the +constructor from an ordinary copy constructor. The basic splitting +constructor should attempt to split ``r`` roughly into two halves, and +update ``r`` to be the first half, and set the constructed object as the +second half. + + +Unlike the basic splitting constructor, the proportional splitting +constructor is optional and takes the second argument of type +``oneapi::tbb::proportional_split``. The type has methods ``left`` and ``right`` +that return the values of the proportion. These values should be used to +split ``r`` accordingly, so that the updated ``r`` corresponds to the +left part of the proportion, and the constructed object corresponds to +the right part. + + +Both splitting constructors should guarantee that the updated ``r`` part +and the constructed object are not empty. The parallel algorithm +templates call the splitting constructors on ``r`` only if +``r.is_divisible`` is true. + + +The iteration space does not have to be linear. Look at +``oneapi/tbb/blocked_range2d.h`` for an example of a range that is +two-dimensional. Its splitting constructor attempts to split the range +along its longest axis. When used with ``parallel_for``, it causes the +loop to be "recursively blocked" in a way that improves cache usage. +This nice cache behavior means that using ``parallel_for`` over a +``blocked_range2d`` can make a loop run faster than the sequential +equivalent, even on a single processor. + +The ``blocked_range2d`` allows you to use different value types for +its first dimension, *rows*, and the second one, *columns*. +That means you can combine indexes, pointers, and iterators into a joint +iteration space. Use the methods ``rows()`` and ``cols()`` to obtain +``blocked_range`` objects that represent the respective dimensions. + +The ``blocked_range3d`` class template extends this approach to 3D by adding +``pages()`` as the first dimension, followed by ``rows()`` and ``cols()``. + +The ``blocked_nd_range`` class template represents a blocked iteration +space of any dimensionality. Unlike the previously described 2D and 3D ranges, +``blocked_nd_range`` uses the same value type for all its axes, and its +constructor requires you to pass N instances of ``blocked_range`` instead of +individual boundary values. The change in the naming pattern reflects these +differences. + + +Example of a Multidimensional Iteration Space +------------------------------------------------ + +The example demonstrates calculation of a 3-dimensional filter over the pack +of feature maps. + +The ``convolution3d`` function iterates over the output cells, assigning to +each cell the result of the ``kernel3d`` function that combines the values +from a range in the feature maps. + +To run the computation in parallel, ``tbb::parallel_for`` is called with +``tbb::blocked_nd_range`` as an argument. The body function processes +the received 3D subrange in nested loops, using the method ``dim`` to get +the loop boundaries for each dimension. + + +.. literalinclude:: ./snippets/blocked_nd_range_example.h + :language: c++ diff --git a/_sources/main/tbb_userguide/Allocator_Configuration.rst b/_sources/main/tbb_userguide/Allocator_Configuration.rst new file mode 100644 index 0000000000..5d0c235ab0 --- /dev/null +++ b/_sources/main/tbb_userguide/Allocator_Configuration.rst @@ -0,0 +1,42 @@ +.. _Allocator_Configuration: + +Configuring the Memory Allocator +================================ + + +The oneTBB memory allocator provides the following API functions and +environment variables to configure its behavior: + + +- the ``scalable_allocation_command`` function instructs the allocator + to perform a certain action, such as cleaning up its internal memory + buffers. + + +- the ``scalable_allocation_mode`` function allows an application to + set certain parameters for the memory allocator, such as an option to + map memory in huge pages or define a recommended heap size. These + settings take effect until modified by another call to + ``scalable_allocation_mode``. + + +Some of the memory allocator parameters can also be set via system +environment variables. It can be useful to adjust the behavior without +modifying application source code, to ensure that a setting takes effect +as early as possible, or to avoid explicit dependency on the oneTBB +allocator binaries. The following environment variables are recognized: + + +- ``TBB_MALLOC_USE_HUGE_PAGES`` controls usage of huge pages for memory + mapping. + + +- ``TBB_MALLOC_SET_HUGE_OBJECT_THRESHOLD`` defines the lower bound for + the size (bytes), that is interpreted as huge and not released during + regular cleanup operations. + + +These variables only take effect at the time the memory manager is +initialized; later environment changes are ignored. A call to +``scalable_allocation_mode`` overrides the effect of the corresponding +environment variable. diff --git a/_sources/main/tbb_userguide/Automatic_Chunking.rst b/_sources/main/tbb_userguide/Automatic_Chunking.rst new file mode 100644 index 0000000000..54ea2df807 --- /dev/null +++ b/_sources/main/tbb_userguide/Automatic_Chunking.rst @@ -0,0 +1,23 @@ +.. _Automatic_Chunking: + +Automatic Chunking +================== + + +A parallel loop construct incurs overhead cost for every chunk of work +that it schedules. |full_name| +chooses chunk sizes automatically, depending upon load balancing +needs. The heuristic attempts to limit overheads while +still providing ample opportunities for load balancing. + + +.. CAUTION:: + Typically a loop needs to take at least a million clock cycles to + make it worth using ``parallel_for``. For example, a loop that takes + at least 500 microseconds on a 2 GHz processor might benefit from + ``parallel_for``. + + +The default automatic chunking is recommended for most uses. As with +most heuristics, however, there are situations where controlling the +chunk size more precisely might yield better performance. diff --git a/_sources/main/tbb_userguide/Bandwidth_and_Cache_Affinity_os.rst b/_sources/main/tbb_userguide/Bandwidth_and_Cache_Affinity_os.rst new file mode 100644 index 0000000000..274b220a2a --- /dev/null +++ b/_sources/main/tbb_userguide/Bandwidth_and_Cache_Affinity_os.rst @@ -0,0 +1,107 @@ +.. _Bandwidth_and_Cache_Affinity: + +Bandwidth and Cache Affinity +============================ + + +For a sufficiently simple function ``Foo``, the examples might not show +good speedup when written as parallel loops. The cause could be +insufficient system bandwidth between the processors and memory. In that +case, you may have to rethink your algorithm to take better advantage of +cache. Restructuring to better utilize the cache usually benefits the +parallel program as well as the serial program. + + +An alternative to restructuring that works in some cases is +``affinity_partitioner.`` It not only automatically chooses the +grainsize, but also optimizes for cache affinity and tries to distribute +the data uniformly among threads. Using ``affinity_partitioner`` can +significantly improve performance when: + + +- The computation does a few operations per data access. + + +- The data acted upon by the loop fits in cache. + + +- The loop, or a similar loop, is re-executed over the same data. + + +- There are more than two hardware threads available (and especially if + the number of threads is not a power of two). If only two threads are + available, the default scheduling in |full_name| + usually provides sufficient cache affinity. + + +The following code shows how to use ``affinity_partitioner``. + + +:: + + + #include "oneapi/tbb.h" +   + + void ParallelApplyFoo( float a[], size_t n ) { + static affinity_partitioner ap; + parallel_for(blocked_range(0,n), ApplyFoo(a), ap); + } +   + + void TimeStepFoo( float a[], size_t n, int steps ) { + for( int t=0; tcancel_group_execution()``. +The part ``current_context()`` references the ``task_group_context*`` of the currently executing task if any on the current thread. +Calling ``cancel_group_execution()`` cancels all tasks in its ``task_group_context``, which is explained in more detail in :ref:`Cancellation_and_Nested_Parallelism`. +The method returns ``true`` if it actually causes cancellation, ``false`` if the ``task_group_context`` was already cancelled. + +The example below shows how to use ``current_context()->cancel_group_execution()``. + +:: + + #include "oneapi/tbb.h" + + #include + #include +   + using namespace oneapi::tbb; + using namespace std; +   + vector Data; +   + struct Update { + void operator()( const blocked_range& r ) const { + for( int i=r.begin(); i!=r.end(); ++i ) + if( icancel_group_execution() ) + cout << "Index " << i << " caused cancellation\n"; + return; + } + } + }; +   + + int main() { + Data.resize(1000); + parallel_for( blocked_range(0, 2000), Update()); + return 0; + } + diff --git a/_sources/main/tbb_userguide/Cancellation_and_Nested_Parallelism.rst b/_sources/main/tbb_userguide/Cancellation_and_Nested_Parallelism.rst new file mode 100644 index 0000000000..28ceca64d0 --- /dev/null +++ b/_sources/main/tbb_userguide/Cancellation_and_Nested_Parallelism.rst @@ -0,0 +1,136 @@ +.. _Cancellation_and_Nested_Parallelism: + +Cancellation and Nested Parallelism +=================================== + + +The discussion so far was simplified by assuming non-nested parallelism +and skipping details of ``task_group_context``. This topic explains +both. + + +An |full_name| algorithm executes +by creating ``task`` objects that execute the snippets of code that you +supply to the algorithm template. By default, these ``task`` objects are +associated with a ``task_group_context`` created by the algorithm. +Nested oneTBB algorithms create a tree of these ``task_group_context`` +objects. Cancelling a ``task_group_context`` cancels all of its child +``task_group_context`` objects, and transitively all its descendants. +Hence an algorithm and all algorithms it called can be cancelled with a +single request. + + +Exceptions propagate upwards. Cancellation propagates downwards. The +opposition interplays to cleanly stop a nested computation when an +exception occurs. For example, consider the tree in the following +figure. Imagine that each node represents an algorithm and its +``task_group_context``. + + +.. container:: fignone + :name: fig6 + + + Tree of task_group_context + |image0| + + +Suppose that the algorithm in C throws an exception and no node catches +the exception. oneTBB propagates the exception upwards, cancelling +related subtrees downwards, as follows: + + +#. Handle exception in C: + + + a. Capture exception in C. + + + b. Cancel tasks in C. + + + c. Throw exception from C to B. + + +#. Handle exception in B: + + + a. Capture exception in B. + + + b. Cancel tasks in B and, by downwards propagation, in D. + + + c. Throw an exception out of B to A. + + +#. Handle exception in A: + + + a. Capture exception in A. + + + b. Cancel tasks in A and, by downwards propagation, in E, F, and G. + + + c. Throw an exception upwards out of A. + + +If your code catches the exception at any level, then oneTBB does not +propagate it any further. For example, an exception that does not escape +outside the body of a ``parallel_for`` does not cause cancellation of +other iterations. + + +To prevent downwards propagation of cancellation into an algorithm, +construct an 'isolated' ``task_group_context`` on the stack and pass it +to the algorithm explicitly. The example uses C++11 lambda expressions for brevity. + + +:: + + + #include "oneapi/tbb.h" +   + + bool Data[1000][1000]; +   + + int main() { + try { + parallel_for( 0, 1000, 1, + []( int i ) { + task_group_context root(task_group_context::isolated); + parallel_for( 0, 1000, 1, + []( int ) { + Data[i][j] = true; + }, + root); + throw "oops"; + }); + } catch(...) { + } + return 0; + } + + +The example performs two parallel loops: an outer loop over ``i`` and +inner loop over ``j``. The creation of the isolated +``task_group_context`` ``root`` protects the inner loop from downwards +propagation of cancellation from the ``i`` loop. When the exception +propagates to the outer loop, any pending ``outer`` iterations are +cancelled, but not inner iterations for an outer iteration that started. +Hence when the program completes, each row of ``Data`` may be different, +depending upon whether its iteration ``i`` ran at all, but within a row, +the elements will be homogeneously ``false`` or ``true``, not a mixture. + + +Removing the blue text would permit cancellation to propagate down into +the inner loop. In that case, a row of ``Data`` might end up with both +``true`` and ``false`` values. + + +.. |image0| image:: Images/image013.jpg + :width: 261px + :height: 131px + diff --git a/_sources/main/tbb_userguide/Concurrent_Queue_Classes.rst b/_sources/main/tbb_userguide/Concurrent_Queue_Classes.rst new file mode 100644 index 0000000000..566ef505dd --- /dev/null +++ b/_sources/main/tbb_userguide/Concurrent_Queue_Classes.rst @@ -0,0 +1,108 @@ +.. _Concurrent_Queue_Classes: + +Concurrent Queue Classes +======================== + + +Template class ``concurrent_queue`` implements a concurrent +queue with values of type ``T``. Multiple threads may simultaneously +push and pop elements from the queue. The queue is unbounded and has no +blocking operations. The fundamental operations on it are ``push`` and +``try_pop``. The ``push`` operation works just like ``push`` for a +std::queue. The operation ``try_pop`` pops an item if it is available. +The check and popping have to be done in a single operation for sake of +thread safety. + + +For example, consider the following serial code: + + +:: + + + extern std::queue MySerialQueue; + T item; + if( !MySerialQueue.empty() ) { + item = MySerialQueue.front(); + MySerialQueue.pop_front(); + ... process item... + } + + +Even if each std::queue method were implemented in a thread-safe manner, +the composition of those methods as shown in the example would not be +thread safe if there were other threads also popping from the same +queue. For example, ``MySerialQueue.empty()`` might return true just +before another thread snatches the last item from ``MySerialQueue``. + + +The equivalent thread-safe |full_name| code is: + + +:: + + + extern concurrent_queue MyQueue; + T item; + if( MyQueue.try_pop(item) ) { + ...process item... + } + + +In a single-threaded program, a queue is a first-in first-out structure. +But if multiple threads are pushing and popping concurrently, the +definition of "first" is uncertain. Use of ``concurrent_queue`` +guarantees that if a thread pushes two values, and another thread pops +those two values, they will be popped in the same order that they were +pushed. + + +Template class ``concurrent_queue`` is unbounded and has no methods that +wait. It is up to the user to provide synchronization to avoid overflow, +or to wait for the queue to become non-empty. Typically this is +appropriate when the synchronization has to be done at a higher level. + + +Template class ``concurrent_bounded_queue`` is a variant that +adds blocking operations and the ability to specify a capacity. The +methods of particular interest on it are: + + +- ``pop(item)`` waits until it can succeed. + + +- ``push(item)`` waits until it can succeed without exceeding the + queue's capacity. + + +- ``try_push(item)`` pushes ``item`` only if it would not exceed the + queue's capacity. + + +- size() returns a *signed* integer. + + +The value of concurrent_queue::size() is defined as the number of push +operations started minus the number of pop operations started. If pops +outnumber pushes, ``size()`` becomes negative. For example, if a +``concurrent_queue`` is empty, and there are ``n`` pending pop +operations, ``size()`` returns -\ ``n``. This provides an easy way for +producers to know how many consumers are waiting on the queue. Method +``empty()`` is defined to be true if and only if ``size()`` is not +positive. + + +By default, a ``concurrent_bounded_queue`` is unbounded. It may hold any +number of values, until memory runs out. It can be bounded by setting +the queue capacity with method ``set_capacity``.Setting the capacity +causes ``push`` to block until there is room in the queue. Bounded +queues are slower than unbounded queues, so if there is a constraint +elsewhere in your program that prevents the queue from becoming too +large, it is better not to set the capacity. If you do not need the +bounds or the blocking pop, consider using ``concurrent_queue`` instead. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Iterating_Over_a_Concurrent_Queue_for_Debugging + ../tbb_userguide/When_Not_to_Use_Queues \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Constraints.rst b/_sources/main/tbb_userguide/Constraints.rst new file mode 100644 index 0000000000..1928fe8eeb --- /dev/null +++ b/_sources/main/tbb_userguide/Constraints.rst @@ -0,0 +1,30 @@ +.. _Constraints: + +Constrained APIs +================ + +Starting from C++20, most of |full_name| APIs are constrained to +enforce `named requirements `_ on +template arguments types. + +The violations of these requirements are detected at a compile time during the template instantiation. + +.. rubric:: Example + +.. code:: cpp + + // Call for body(oneapi::tbb::blocked_range) is ill-formed + // oneapi::tbb::parallel_for call results in constraint failure + auto body = [](const int& r) { /*...*/ }; + oneapi::tbb::parallel_for(oneapi::tbb::blocked_range{1, 10}, body); + + // Error example: + // error: no matching function to call to oneapi::tbb::parallel_for + // note: constraints not satisfied + // note: the required expression 'body(range)' is invalid + body(range); + +.. caution:: + + The code that violates named requirements but compiles successfully until C++20, + may not compile in C++20 mode due to early and strict constraints diagnostics. diff --git a/_sources/main/tbb_userguide/Containers.rst b/_sources/main/tbb_userguide/Containers.rst new file mode 100644 index 0000000000..aa815ffeeb --- /dev/null +++ b/_sources/main/tbb_userguide/Containers.rst @@ -0,0 +1,55 @@ +.. _Containers: + +Containers +========== + + +|full_name| provides highly concurrent +container classes. These containers can be used with raw Windows\* OS or +Linux\* OS threads, or in conjunction with task-based programming. + + +A concurrent container allows multiple threads to concurrently access +and update items in the container. Typical C++ STL containers do not +permit concurrent update; attempts to modify them concurrently often +result in corrupting the container. STL containers can be wrapped in a +mutex to make them safe for concurrent access, by letting only one +thread operate on the container at a time, but that approach eliminates +concurrency, thus restricting parallel speedup. + + +Containers provided by oneTBB offer a much higher level of concurrency, +via one or both of the following methods: + + +- **Fine-grained locking:** Multiple threads operate on the container + by locking only those portions they really need to lock. As long as + different threads access different portions, they can proceed + concurrently. + + +- **Lock-free techniques:** Different threads account and correct for + the effects of other interfering threads. + + +Notice that highly-concurrent containers come at a cost. They typically +have higher overheads than regular STL containers. Operations on +highly-concurrent containers may take longer than for STL containers. +Therefore, use highly-concurrent containers when the speedup from the +additional concurrency that they enable outweighs their slower +sequential performance. + + +.. CAUTION:: + As with most objects in C++, the constructor or destructor of a + container object must not be invoked concurrently with another + operation on the same object. Otherwise the resulting race may cause + the operation to be executed on an undefined object. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/concurrent_hash_map + ../tbb_userguide/concurrent_vector_ug + ../tbb_userguide/Concurrent_Queue_Classes + ../tbb_userguide/Summary_of_Containers diff --git a/_sources/main/tbb_userguide/Controlling_Chunking_os.rst b/_sources/main/tbb_userguide/Controlling_Chunking_os.rst new file mode 100644 index 0000000000..999419d298 --- /dev/null +++ b/_sources/main/tbb_userguide/Controlling_Chunking_os.rst @@ -0,0 +1,170 @@ +.. _Controlling_Chunking: + +Controlling Chunking +==================== + + +Chunking is controlled by a *partitioner* and a *grainsize.*\ To gain +the most control over chunking, you specify both. + + +- Specify ``simple_partitioner()`` as the third argument to + ``parallel_for``. Doing so turns off automatic chunking. + + +- Specify the grainsize when constructing the range. The thread + argument form of the constructor is + ``blocked_range(begin,end,grainsize)``. The default value of + ``grainsize`` is 1. It is in units of loop iterations per chunk. + + +If the chunks are too small, the overhead may exceed the performance +advantage. + + +The following code is the last example from parallel_for, modified to +use an explicit grainsize ``G``. + + +:: + + + #include "oneapi/tbb.h" +   + + void ParallelApplyFoo( float a[], size_t n ) { + parallel_for(blocked_range(0,n,G), ApplyFoo(a), + simple_partitioner()); + } + + +The grainsize sets a minimum threshold for parallelization. The +``parallel_for`` in the example invokes ``ApplyFoo::operator()`` on +chunks, possibly of different sizes. Let *chunksize* be the number of +iterations in a chunk. Using ``simple_partitioner`` guarantees that +[G/2] <= *chunksize* <= G. + + +There is also an intermediate level of control where you specify the +grainsize for the range, but use an ``auto_partitioner`` and +``affinity_partitioner``. An ``auto_partitioner`` is the default +partitioner. Both partitioners implement the automatic grainsize +heuristic described in :ref:`Automatic_Chunking`. An +``affinity_partitioner`` implies an additional hint, as explained later +in Section :ref:`Bandwidth_and_Cache_Affinity`. Though these partitioners +may cause chunks to have more than G iterations, they never generate +chunks with less than [G/2] iterations. Specifying a range with an +explicit grainsize may occasionally be useful to prevent these +partitioners from generating wastefully small chunks if their heuristics +fail. + + +Because of the impact of grainsize on parallel loops, it is worth +reading the following material even if you rely on ``auto_partitioner`` +and ``affinity_partitioner`` to choose the grainsize automatically. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - |image0| + - |image1| + * - Case A + - Case B + + + + +The above figure illustrates the impact of grainsize by showing the +useful work as the gray area inside a brown border that represents +overhead. Both Case A and Case B have the same total gray area. Case A +shows how too small a grainsize leads to a relatively high proportion of +overhead. Case B shows how a large grainsize reduces this proportion, at +the cost of reducing potential parallelism. The overhead as a fraction +of useful work depends upon the grainsize, not on the number of grains. +Consider this relationship and not the total number of iterations or +number of processors when setting a grainsize. + + +A rule of thumb is that ``grainsize`` iterations of ``operator()`` +should take at least 100,000 clock cycles to execute. For example, if a +single iteration takes 100 clocks, then the ``grainsize`` needs to be at +least 1000 iterations. When in doubt, do the following experiment: + + +#. Set the ``grainsize`` parameter higher than necessary. The grainsize + is specified in units of loop iterations. If you have no idea of how + many clock cycles an iteration might take, start with + ``grainsize``\ =100,000. The rationale is that each iteration + normally requires at least one clock per iteration. In most cases, + step 3 will guide you to a much smaller value. + + +#. Run your algorithm. + + +#. Iteratively halve the ``grainsize`` parameter and see how much the + algorithm slows down or speeds up as the value decreases. + + +A drawback of setting a grainsize too high is that it can reduce +parallelism. For example, if the grainsize is 1000 and the loop has 2000 +iterations, the ``parallel_for`` distributes the loop across only two +processors, even if more are available. However, if you are unsure, err +on the side of being a little too high instead of a little too low, +because too low a value hurts serial performance, which in turns hurts +parallel performance if there is other parallelism available higher up +in the call tree. + + +.. tip:: + You do not have to set the grainsize too precisely. + + +The next figure shows the typical "bathtub curve" for execution time +versus grainsize, based on the floating point ``a[i]=b[i]*c`` +computation over a million indices. There is little work per iteration. +The times were collected on a four-socket machine with eight hardware +threads. + + +.. container:: fignone + :name: fig2 + + + Wall Clock Time Versus Grainsize + |image2| + + +The scale is logarithmic. The downward slope on the left side indicates +that with a grainsize of one, most of the overhead is parallel +scheduling overhead, not useful work. An increase in grainsize brings a +proportional decrease in parallel overhead. Then the curve flattens out +because the parallel overhead becomes insignificant for a sufficiently +large grainsize. At the end on the right, the curve turns up because the +chunks are so large that there are fewer chunks than available hardware +threads. Notice that a grainsize over the wide range 100-100,000 works +quite well. + + +.. tip:: + A general rule of thumb for parallelizing loop nests is to + parallelize the outermost one possible. The reason is that each + iteration of an outer loop is likely to provide a bigger grain of + work than an iteration of an inner loop. + + + +.. |image0| image:: Images/image002.jpg + :width: 161px + :height: 163px +.. |image1| image:: Images/image004.jpg + :width: 157px + :height: 144px +.. |image2| image:: Images/image006.jpg + :width: 462px + :height: 193px + diff --git a/_sources/main/tbb_userguide/Cook_Until_Done_parallel_do.rst b/_sources/main/tbb_userguide/Cook_Until_Done_parallel_do.rst new file mode 100644 index 0000000000..1ef6b61b48 --- /dev/null +++ b/_sources/main/tbb_userguide/Cook_Until_Done_parallel_do.rst @@ -0,0 +1,84 @@ +.. _Cook_Until_Done_parallel_do: + +Cook Until Done: parallel_for_each +================================== + + +For some loops, the end of the iteration space is not known in advance, +or the loop body may add more iterations to do before the loop exits. +You can deal with both situations using the template class ``oneapi::tbb::parallel_for_each``. + + +A linked list is an example of an iteration space that is not known in +advance. In parallel programming, it is usually better to use dynamic +arrays instead of linked lists, because accessing items in a linked list +is inherently serial. But if you are limited to linked lists, the items +can be safely processed in parallel, and processing each item takes at +least a few thousand instructions, you can use ``parallel_for_each`` to +gain some parallelism. + + +For example, consider the following serial code: + + +:: + + + void SerialApplyFooToList( const std::list& list ) { + for( std::list::const_iterator i=list.begin() i!=list.end(); ++i ) + Foo(*i); + } + + +If ``Foo`` takes at least a few thousand instructions to run, you can +get parallel speedup by converting the loop to use +``parallel_for_each``. To do so, define an object with a ``const`` +qualified ``operator()``. This is similar to a C++ function object from +the C++ standard header ````, except that ``operator()`` +must be ``const``. + + +:: + + + class ApplyFoo { + public: + void operator()( Item& item ) const { + Foo(item); + } + }; + + +The parallel form of ``SerialApplyFooToList`` is as follows: + + +:: + + + void ParallelApplyFooToList( const std::list& list ) { + parallel_for_each( list.begin(), list.end(), ApplyFoo() ); + } + + +An invocation of ``parallel_for_each`` never causes two threads to act +on an input iterator concurrently. Thus typical definitions of input +iterators for sequential programs work correctly. This convenience makes +``parallel_for_each`` unscalable, because the fetching of work is +serial. But in many situations, you still get useful speedup over doing +things sequentially. + + +There are two ways that ``parallel_for_each`` can acquire work scalably. + + +- The iterators can be random-access iterators. + + +- The body argument to ``parallel_for_each``, if it takes a second + argument *feeder* of type ``parallel_for_each&``, can add more + work by calling ``feeder.add(item)``. For example, suppose processing + a node in a tree is a prerequisite to processing its descendants. + With ``parallel_for_each``, after processing a node, you could use + ``feeder.add`` to add the descendant nodes. The instance of + ``parallel_for_each`` does not terminate until all items have been + processed. diff --git a/_sources/main/tbb_userguide/Data_Flow_Graph.rst b/_sources/main/tbb_userguide/Data_Flow_Graph.rst new file mode 100644 index 0000000000..a8d1aa715b --- /dev/null +++ b/_sources/main/tbb_userguide/Data_Flow_Graph.rst @@ -0,0 +1,198 @@ +.. _Data_Flow_Graph: + +Data Flow Graph +=============== + + +In a data flow graph, nodes are computations that send and receive data +messages. Some nodes may only send messages, others may only receive +messages, and others may send messages in response to messages that they +receive. + + +In the following data flow graph, the left-most node generates the +integer values from 1 to 10 and passes them to two successor nodes. One +of the successors squares each value it receives and passes the result +downstream. The second successor cubes each value it receives and passes +the result downstream. The right-most node receives values from both of +the middle nodes. As it receives each value, it adds it to a running sum +of values. When the application is run to completion, the value of sum +will be equal to the sum of the sequence of squares and cubes from 1 to +10. + + +.. container:: fignone + :name: simple_data_flow_title + + + Simple Data Flow Graph + + + .. container:: imagecenter + + + |image0| + + +The following code snippet shows an implementation of the **Simple Data +Flow Graph** shown above: + + +:: + + + int sum = 0; + graph g; + function_node< int, int > squarer( g, unlimited, [](const int &v) { + return v*v; + } ); + function_node< int, int > cuber( g, unlimited, [](const int &v) { + return v*v*v; + } ); + function_node< int, int > summer( g, 1, [&](const int &v ) -> int { + return sum += v; + } ); + make_edge( squarer, summer ); + make_edge( cuber, summer ); + + + for ( int i = 1; i <= 10; ++i ) { + squarer.try_put(i); + cuber.try_put(i); + } + g.wait_for_all(); + + + cout << "Sum is " << sum << "\n"; + + +In the implementation above, the following function_nodes are created: + + +- one to square values +- one to cube values +- one to add values to the global sum + + +Since the squarer and cuber nodes are side-effect free, they are created +with an unlimited concurrency. The summer node updates the sum through a +reference to a global variable and therefore is not safe to execute in +parallel. It is therefore created with a concurrency limit of 1. The +node F from **Simple Data Flow Graph** above is implemented as a loop +that puts messages to both the squarer and cuber node. + + +A slight improvement over the first implementation is to introduce an +additional node type, a ``broadcast_node``. A ``broadcast_node`` broadcasts any +message it receives to all of its successors. + + +This enables replacing the two ``try_put``'s in the loop with a single +``try_put``: + + +:: + + + broadcast_node b(g); + make_edge( b, squarer ); + make_edge( b, cuber ); + for ( int i = 1; i <= 10; ++i ) { + b.try_put(i); + } + g.wait_for_all(); + + +An even better option, which will make the implementation even more like +the **Simple Data Flow Graph** above, is to introduce an ``input_node``. An +``input_node``, as the name implies only sends messages and does not +receive messages. Its constructor takes two arguments: + + +:: + + + template< typename Body > input_node( graph &g, Body body) + +The body is a function object, or lambda expression, that contains a +function operator: + + +:: + + + Output Body::operator()( oneapi::tbb::flow_control &fc ); + + +You can replace the loop in the example with an ``input_node`` + + +:: + + + input_node< int > src( g, src_body(10) ); + make_edge( src, squarer ); + make_edge( src, cuber ); + src.activate(); + g.wait_for_all(); + + +The runtime library will repeatedly invoke the function operator in +``src_body`` until ``fc.stop()`` is invoked inside the body. You therefore +need to create body that will act like the body of the loop in the **Simple Data Flow Graph** +above. The final implementation after all of these changes is shown +below: + + +:: + + + class src_body { + const int my_limit; + int my_next_value; + public: + src_body(int l) : my_limit(l), my_next_value(1) {} + int operator()( oneapi::tbb::flow_control& fc ) { + if ( my_next_value <= my_limit ) { + return my_next_value++; + } else { + fc.stop(); + return int(); + } + } + }; + + + int main() { + int sum = 0; + graph g; + function_node< int, int > squarer( g, unlimited, [](const int &v) { + return v*v; + } ); + function_node< int, int > cuber( g, unlimited, [](const int &v) { + return v*v*v; + } ); + function_node< int, int > summer( g, 1, [&](const int &v ) -> int { + return sum += v; + } ); + make_edge( squarer, summer ); + make_edge( cuber, summer ); + input_node< int > src( g, src_body(10) ); + make_edge( src, squarer ); + make_edge( src, cuber ); + src.activate(); + g.wait_for_all(); + cout << "Sum is " << sum << "\n"; + } + + +This final implementation has all of the nodes and edges from the +**Simple Data Flow Graph** above. In this simple example, there is not +much advantage in using an ``input_node`` over an explicit loop. But, +because an ``input_node`` is able to react to the behavior of downstream +nodes, it can limit memory use in more complex graphs. For more +information, see:ref:`create_token_based_system` . + + +.. |image0| image:: Images/flow_graph.jpg + diff --git a/_sources/main/tbb_userguide/Debug_Versus_Release_Libraries.rst b/_sources/main/tbb_userguide/Debug_Versus_Release_Libraries.rst new file mode 100644 index 0000000000..654724615f --- /dev/null +++ b/_sources/main/tbb_userguide/Debug_Versus_Release_Libraries.rst @@ -0,0 +1,56 @@ +.. _Debug_Versus_Release_Libraries: + +Debug Versus Release Libraries +============================== + + +The following table details the |full_name| +dynamic shared libraries that come in debug and release +versions. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Library + - Description + - When to Use + * - | ``tbb_debug`` + | ``tbbmalloc_debug`` + | ``tbbmalloc_proxy_debug`` + | ``tbbbind_debug`` + - These versions have extensive internal checking for correct use of the library. + - Use with code that is compiled with the macro ``TBB_USE_DEBUG`` set to 1. + * - | ``tbb`` + | ``tbbmalloc`` + | ``tbbmalloc_proxy`` + | ``tbbbind`` + - These versions deliver top performance. They eliminate most checking for correct use of the library. + - Use with code compiled with ``TBB_USE_DEBUG`` undefined or set to zero. + +.. tip:: + Test your programs with the debug versions of the libraries first, to + assure that you are using the library correctly.  With the release + versions, incorrect usage may result in unpredictable program + behavior. + + +oneTBB supports Intel® Inspector, Intel® VTune™ Profiler and Intel® Advisor. +Full support of these tools requires compiling with macro ``TBB_USE_PROFILING_TOOLS=1``. +That symbol defaults to 1 in the following conditions: + +- When ``TBB_USE_DEBUG=1``. +- On the Microsoft Windows\* operating system, when ``_DEBUG=1``. + +The :ref:`reference` section explains the default values in more detail. + + +.. CAUTION:: + The instrumentation support for Intel® Inspector becomes live after + the first initialization of the task library. If the library + components are used before this initialization occurs, Intel® Inspector + may falsely report race conditions that are not really races. + diff --git a/_sources/main/tbb_userguide/Dependence_Graph.rst b/_sources/main/tbb_userguide/Dependence_Graph.rst new file mode 100644 index 0000000000..bbe0245052 --- /dev/null +++ b/_sources/main/tbb_userguide/Dependence_Graph.rst @@ -0,0 +1,147 @@ +.. _Dependence_Graph: + +Dependence Graph +================ + + +In a dependence graph, the nodes invoke body objects to perform +computations and the edges create a partial ordering of these +computations. At runtime, the library spawns and schedules tasks to +execute the body objects when it is legal to do so according to the +specified partial ordering. The following figure shows an example of an +application that could be expressed using a dependence graph. + + +.. container:: fignone + :name: dependence_graph_make_sandwitch + + + Dependence Graph for Making a Sandwich + + + .. container:: imagecenter + + + |image0| + + +Dependence graphs are a special case of data flow graphs, where the data +passed between nodes are of type oneapi::tbb::flow::continue_msg. Unlike a +general data flow graph, nodes in a dependence graph do not spawn a task +for each message they receive. Instead, they are aware of the number of +predecessors they have, count the messages they receive and only spawn a +task to execute their body when this count is equal to the total number +of their predecessors. + + +The following figure shows another example of a dependence graph. It has +the same topology as the figure above, but with simple functions +replacing the sandwich making steps. In this partial ordering, function +A must complete executing before any other computation starts executing. +Function B must complete before C and D start executing; and E must +complete before D and F start executing. This is a partial ordering +because, for example, there is no explicit ordering requirement between +B and E or C and F. + + +.. container:: fignone + :name: simple_dependence_graph + + + Simple Dependence Graph + + + .. container:: imagecenter + + + |image1| + + +To implement this as a flow graph, continue_node objects are used for +the nodes and continue_msg objects as the messages. A continue_node +constructor takes two arguments: + + +:: + + + template< typename Body > continue_node( graph &g, Body body) + + +The first argument is the graph it belongs to and the second is a +function object or lambda expression. Unlike a function_node, a +continue_node is always assumed to have unlimited concurrency and will +immediately spawn a task whenever its dependencies are met. + + +The following code snippet is an implementation of the example in this +figure. + + +:: + + + typedef continue_node< continue_msg > node_t; + typedef const continue_msg & msg_t; + + + int main() { + oneapi::tbb::flow::graph g; + node_t A(g, [](msg_t){ a(); } ); + node_t B(g, [](msg_t){ b(); } ); + node_t C(g, [](msg_t){ c(); } ); + node_t D(g, [](msg_t){ d(); } ); + node_t E(g, [](msg_t){ e(); } ); + node_t F(g, [](msg_t){ f(); } ); + make_edge(A, B); + make_edge(B, C); + make_edge(B, D); + make_edge(A, E); + make_edge(E, D); + make_edge(E, F); + A.try_put( continue_msg() ); + g.wait_for_all(); + return 0; + } + + +One possible execution of this graph is shown below. The execution of D +does not start until both B and E are finished. While a task is waiting +in the wait_for_all, its thread can participate in executing other tasks +from the oneTBB work pool. + + +.. container:: fignone + + + Execution Timeline for a Dependence Graph + + + .. container:: imagecenter + + + |image2| + + +Again, it is important to note that all execution in the flow graph +happens asynchronously. The call to A.try_put returns control to the +calling thread quickly, after incrementing the counter and spawning a +task to execute the body of A. Likewise, the body tasks execute the +lambda expressions and then put a continue_msg to all successor nodes, +if any. Only the call to wait_for_all blocks, as it should, and even in +this case the calling thread may be used to execute tasks from the +oneTBB work pool while it is waiting. + + +The above timeline shows the sequence when there are enough threads to +execute all of the tasks that can be executed concurrently in parallel. +If there are fewer threads, then some tasks that are spawned will need +to wait until a thread is available to execute them. + + +.. |image0| image:: Images/flow_graph_complex.jpg + :width: 440px + :height: 337px +.. |image1| image:: Images/dependence_graph.jpg +.. |image2| image:: Images/execution_timeline_dependence.jpg + diff --git a/_sources/main/tbb_userguide/Edges.rst b/_sources/main/tbb_userguide/Edges.rst new file mode 100644 index 0000000000..ea4c214b06 --- /dev/null +++ b/_sources/main/tbb_userguide/Edges.rst @@ -0,0 +1,46 @@ +.. _Edges: + +Flow Graph Basics: Edges +======================== + + +Most applications contain multiple nodes with edges connecting them to +each other. In the flow graph interface, edges are directed channels +over which messages are passed. They are created by calling the function +``make_edge( p, s )`` with two arguments: ``p``, the predecessor, and ``s``, the +successor. You can modify the example used in the **Nodes** topic to +include a second node that squares the value it receives before printing +it and then connect that to the first node with an edge. + + +:: + + + graph g; + function_node< int, int > n( g, unlimited, []( int v ) -> int { + cout << v; + spin_for( v ); + cout << v; + return v; + } ); + function_node< int, int > m( g, 1, []( int v ) -> int { + v *= v; + cout << v; + spin_for( v ); + cout << v; + return v; + } ); + make_edge( n, m ); + n.try_put( 1 ); + n.try_put( 2 ); + n.try_put( 3 ); + g.wait_for_all(); + + +Now there are two ``function_node`` ``s``, ``n`` and ``m``. The call to ``make_edge`` creates +an edge from ``n`` to ``m``. The node ``n`` is created with unlimited concurrency, +while ``m`` has a concurrency limit of 1. The invocations of ``n`` can all +proceed in parallel, while the invocations of ``m`` will be serialized. +Because there is an edge from ``n`` to ``m``, each value ``v``, returned by ``n``, will +be automatically passed to node ``m`` by the runtime library. + diff --git a/_sources/main/tbb_userguide/Exceptions_and_Cancellation.rst b/_sources/main/tbb_userguide/Exceptions_and_Cancellation.rst new file mode 100644 index 0000000000..290f2f2cc3 --- /dev/null +++ b/_sources/main/tbb_userguide/Exceptions_and_Cancellation.rst @@ -0,0 +1,76 @@ +.. _Exceptions_and_Cancellation: + +Exceptions and Cancellation +=========================== + + +|full_name| supports exceptions and +cancellation. When code inside an oneTBB algorithm throws an exception, +the following steps generally occur: + + +#. The exception is captured. Any further exceptions inside the + algorithm are ignored. + + +#. The algorithm is cancelled. Pending iterations are not executed. If + there is oneTBB parallelism nested inside, the nested parallelism may + also be cancelled as explained in :ref:`Cancellation_and_Nested_Parallelism`. + + +#. Once all parts of the algorithm stop, an exception is thrown on the + thread that invoked the algorithm. + + +As compilers evolve to support this functionality, future versions of +oneTBB might throw the original exception. So be sure your code can +catch either type of exception. The following example demonstrates +exception handling: + + + +:: + + + #include "oneapi/tbb.h" + #include + #include +   + + using namespace oneapi::tbb; + using namespace std; +   + + vector Data; +   + + struct Update { + void operator()( const blocked_range& r ) const { + for( int i=r.begin(); i!=r.end(); ++i ) + Data.at(i) += 1; + } + }; +   + + int main() { + Data.resize(1000); + try { + parallel_for( blocked_range(0, 2000), Update()); + } catch( out_of_range& ex ) { + cout << "out_of_range: " << ex.what() << endl; + } + return 0; + } + + +The ``parallel_for`` attempts to iterate over 2000 elements of a vector +with only 1000 elements. Hence the expression ``Data.at(i)`` sometimes +throws an exception ``std::out_of_range`` during execution of the +algorithm. When the exception happens, the algorithm is cancelled and an +exception thrown at the call site to ``parallel_for``. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Cancellation_Without_An_Exception + ../tbb_userguide/Cancellation_and_Nested_Parallelism diff --git a/_sources/main/tbb_userguide/Floating_Point_Settings.rst b/_sources/main/tbb_userguide/Floating_Point_Settings.rst new file mode 100644 index 0000000000..4618f56ae5 --- /dev/null +++ b/_sources/main/tbb_userguide/Floating_Point_Settings.rst @@ -0,0 +1,60 @@ +.. _Floating_Point_Settings: + +Floating-point Settings +======================= + +To propagate CPU-specific settings for floating-point computations to tasks executed by the task scheduler, you can use one of the following two methods: + +* When a ``task_arena`` or a task scheduler for a given application thread is initialized, they capture the current floating-point settings of the thread. +* The ``task_group_context`` class has a method to capture the current floating-point settings. + +By default, worker threads use floating-point settings obtained during the initialization of a ``task_arena`` or the implicit arena of the application thread. The settings are applied to all computations within that ``task_arena`` or started by that application thread. + + +For better control over floating point behavior, a thread may capture the current settings in a task group context. Do it at context creation with a special flag passed to the constructor: + +:: + + task_group_context ctx( task_group_context::isolated, + task_group_context::default_traits | task_group_context::fp_settings ); + + +Or call the ``capture_fp_settings`` method: + +:: + + task_group_context ctx; + ctx.capture_fp_settings(); + + +You can then pass the task group context to most parallel algorithms, including ``flow::graph``, to ensure that all tasks related to this algorithm use the specified floating-point settings. +It is possible to execute the parallel algorithms with different floating-point settings captured to separate contexts, even at the same time. + +Floating-point settings captured to a task group context prevail over the settings captured during task scheduler initialization. It means, if a context is passed to a parallel algorithm, the floating-point settings captured to the context are used. +Otherwise, if floating-point settings are not captured to the context, or a context is not explicitly specified, the settings captured during the task arena initialization are used. + +In a nested call to a parallel algorithm that does not use the context of a task group with explicitly captured floating-point settings, the outer-level settings are used. +If none of the outer-level contexts capture floating-point settings, the settings captured during task arena initialization are used. + +It guarantees that: + +* Floating-point settings are applied to all tasks executed within a task arena, if they are captured: + + * To a task group context. + * During the arena initialization. + +* A call to a oneTBB parallel algorithm does not change the floating-point settings of the calling thread, even if the algorithm uses different settings. + +.. note:: + The guarantees above apply only to the following conditions: + + * A user code inside a task should: + + * Not change the floating-point settings. + * Revert any modifications. + * Restore previous settings before the end of the task. + + * oneTBB task scheduler observers are not used to set or modify floating point settings. + + Otherwise, the stated guarantees are not valid and the behavior related to floating-point settings is undefined. + diff --git a/_sources/main/tbb_userguide/Flow-Graph-exception-tips.rst b/_sources/main/tbb_userguide/Flow-Graph-exception-tips.rst new file mode 100644 index 0000000000..88b0aea35d --- /dev/null +++ b/_sources/main/tbb_userguide/Flow-Graph-exception-tips.rst @@ -0,0 +1,17 @@ +.. _Flow_Graph_exception_tips: + +Flow Graph Tips for Exception Handling and Cancellation +======================================================= + + +The execution of a flow graph can be canceled directly or as a result of +an exception that propagates beyond a node's body. You can then +optionally reset the graph so that it can be re-executed. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/catching_exceptions + ../tbb_userguide/cancel_a_graph + ../tbb_userguide/use_graph_reset + ../tbb_userguide/cancelling_nested_parallelism diff --git a/_sources/main/tbb_userguide/Flow-Graph-waiting-tips.rst b/_sources/main/tbb_userguide/Flow-Graph-waiting-tips.rst new file mode 100644 index 0000000000..176fe7bb89 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow-Graph-waiting-tips.rst @@ -0,0 +1,11 @@ +.. _Flow_Graph_waiting_tips: + +Flow Graph Tips for Waiting for and Destroying a Flow Graph +=========================================================== + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/always_use_wait_for_all + ../tbb_userguide/avoid_dynamic_node_removal + ../tbb_userguide/destroy_graphs_outside_main_thread diff --git a/_sources/main/tbb_userguide/Flow_Graph.rst b/_sources/main/tbb_userguide/Flow_Graph.rst new file mode 100644 index 0000000000..3daf297092 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph.rst @@ -0,0 +1,14 @@ +.. _Flow_Graph: + +Parallelizing Data Flow and Dependence Graphs +============================================= + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Parallelizing_Flow_Graph + ../tbb_userguide/Basic_Flow_Graph_concepts + ../tbb_userguide/Graph_Main_Categories + ../tbb_userguide/Predefined_Node_Types + ../tbb_userguide/Flow_Graph_Tips + ../tbb_userguide/estimate_flow_graph_performance diff --git a/_sources/main/tbb_userguide/Flow_Graph_Buffering_in_Nodes.rst b/_sources/main/tbb_userguide/Flow_Graph_Buffering_in_Nodes.rst new file mode 100644 index 0000000000..232e92efe2 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_Buffering_in_Nodes.rst @@ -0,0 +1,42 @@ +.. _Flow_Graph_Buffering_in_Nodes: + +Flow Graph Basics: Buffering and Forwarding +=========================================== + + +|full_name| flow graph nodes use messages +to communicate data and to enforce dependencies. If a node passes a +message successfully to any successor, no further action is taken with +the message by that node. As noted in the section on Single-push vs. +Broadcast-push, a message may be passed to one or to multiple +successors, depending on the type of the node, how many successors are +connected to the node, and whether the message is pushed or pulled. + + +There are times when a node cannot successfully push a message to any +successor. In this case what happens to the message depends on the type +of the node. The two possibilities are: + + +- The node stores the message to be forwarded later. +- The node discards the message. + + +If a node discards messages that are not forwarded, and this behavior is +not desired, the node should be connected to a buffering node that does +store messages that cannot be pushed. + + +If a message has been stored by a node, there are two ways it can be +passed to another node: + + +- A successor to the node can pull the message using ``try_get()`` or + ``try_reserve()``. +- A successor can be connected using ``make_edge()``. + + +If a ``try_get()`` successfully forwards a message, it is removed from +the node that stored it. If a node is connected using ``make_edge`` the +node will attempt to push a stored message to the new successor. + diff --git a/_sources/main/tbb_userguide/Flow_Graph_Message_Passing_Protocol.rst b/_sources/main/tbb_userguide/Flow_Graph_Message_Passing_Protocol.rst new file mode 100644 index 0000000000..7e7e14eb7b --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_Message_Passing_Protocol.rst @@ -0,0 +1,47 @@ +.. _Flow_Graph_Message_Passing_Protocol: + +Flow Graph Basics: Message Passing Protocol +=========================================== + + +|full_name| flow graph operates by passing +messages between nodes. A node may not be able to receive and process a +message from its predecessor. For a graph to operate most-efficiently, +if this occurs the state of the edge between the nodes can change its +state to pull so when the successor is able to handle a message it can +query its predecessor to see if a message is available. If the edge did +not reverse from push to pull, the predecessor node would have to +repeatedly attempt to forward its message until the successor accepts +it. This would consume resources needlessly. + + +Once the edge is in pull mode, when the successor is not busy, it will +try to pull a message from a predecessor. + + +#. If a predecessor has a message, the successor will process it and the + edge will remain in pull mode. +#. If the predecessor has no message, the edge between the nodes will + switch from pull to push mode. + + +The state diagram of this Push-Pull protocol is: + + +.. container:: fignone + :name: basic_push_pull + + + **The dynamic push / pull protocol.** + + + .. container:: imagecenter + + + |image0| + + +.. |image0| image:: Images/flow_graph_message_passing_protocol.jpg + :width: 442px + :height: 196px + diff --git a/_sources/main/tbb_userguide/Flow_Graph_Reservation.rst b/_sources/main/tbb_userguide/Flow_Graph_Reservation.rst new file mode 100644 index 0000000000..44fc2f0a98 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_Reservation.rst @@ -0,0 +1,286 @@ +.. _Flow_Graph_Reservation: + +Flow Graph Basics: Reservation +============================== + + +|full_name| flow graph +``join_node`` has four possible policies: ``queueing``, ``reserving``, +``key_matching`` and ``tag_matching``. ``join_nodes`` need messages at +every input before they can create an output message. The reserving +``join_node`` does not have internal buffering, and it does not pull +messages from its inputs until it has a message at each input. To create +an output message it temporarily reserves a message at each input port, +and only if all input ports succeed reserving messages will an output +message be created. If any input port fails to reserve a message, no +message will be pulled by the ``join_node``. + + +To support the reserving ``join_node`` some nodes support +**reservation** of their outputs. The way reservation works is: + + +- When a node connected to a reserving ``join_node`` in push state + tries to push a message, the ``join_node`` always rejects the push + and the edge connecting the nodes is switched to pull mode. +- The reserving input port calls ``try_reserve`` on each edge in pull + state. This may fail; if so, the reserving input port switches that + edge to push state, and tries to reserve the next node connected by + an edge in pull state. While the input port's predecessor is in + reserved state, no other node can retrieve the reserved value. +- If each input port successfully reserves an edge in pull state, the + reserving ``join_node`` will create a message using the reserved + messages and try to push the resulting message to any nodes connected + to it. +- If the message is successfully pushed to a successor, the + predecessors that were reserved are signaled that the messages were + used (by calling ``try_consume()``.) Those messages will be discarded + by the predecessor nodes, because they have been successfully pushed. +- If the message was not successfully pushed to any successor, the + predecessors that were reserved are signaled that the messages were + not used (by calling ``try_release()``.) At this point, the messages + may be pushed to or pulled by other nodes. + + +Because the reserving ``join_node`` will only attempt to push when each +input port has at least one edge in a pull state, and will only attempt +to create and push a message if all input ports succeed reserving +messages, at least one of the predecessors to each of the reserving +``join_node`` input ports must be reservable. + + +The following example demonstrates a reserving ``join_node``'s behavior. +``buffer_nodes`` buffer their output, so they accept a switch of their +output edge from push to pull mode. ``broadcast_nodes`` do not buffer +messages and do not support ``try_get()`` or ``try_reserve()``. + + +:: + + + void run_example2() { // example for Flow_Graph_Reservation.xml + graph g; + broadcast_node bn(g); + buffer_node buf1(g); + buffer_node buf2(g); + typedef join_node, reserving> join_type; + join_type jn(g); + buffer_node buf_out(g); + join_type::output_type tuple_out; + int icnt; + + + // join_node predecessors are both reservable buffer_nodes + make_edge(buf1,input_port<0>(jn)); + make_edge(bn,input_port<0>(jn)); // attach a broadcast_node + make_edge(buf2,input_port<1>(jn)); + make_edge(jn, buf_out); + bn.try_put(2); + buf1.try_put(3); + buf2.try_put(4); + buf2.try_put(7); + g.wait_for_all(); + while (buf_out.try_get(tuple_out)) { + printf("join_node output == (%d,%d)\n",get<0>(tuple_out), get<1>(tuple_out) ); + } + if(buf1.try_get(icnt)) printf("buf1 had %d\n", icnt); + else printf("buf1 was empty\n"); + if(buf2.try_get(icnt)) printf("buf2 had %d\n", icnt); + else printf("buf2 was empty\n"); + } + + +In the example above, port 0 of the reserving ``join_node`` ``jn`` has +two predecessors: a ``buffer_node`` ``buf1`` and a ``broadcast_node`` +``bn``. Port 1 of the ``join_node`` has one predecessor, ``buffer_node`` +``buf2``. + + +.. container:: fignone + :name: reserve_step1 + + + .. container:: imagecenter + + + |image0| + + +We will discuss one possible execution sequence (the scheduling of tasks +may differ slightly, but the end result will be the same.) + + +:: + + + bn.try_put(2); + + +``bn`` attempts to forward 2 to ``jn``. ``jn`` does not accept the value +and the arc from ``bn`` to ``jn`` reverses. Because neither bn nor jn +buffer messages, the message is dropped. Because not all the inputs to +``jn`` have available predecessors, ``jn`` does nothing further. + + +.. CAUTION:: + Any node which does not support reservation will not work correctly + when attached to a reserving ``join_node``. This program demonstrates + why this occurs; connecting non-reserving nodes to nodes requiring + support for reservation is **not** recommended practice. + + +.. container:: fignone + :name: reserve_step2 + + + .. container:: imagecenter + + + |image1| + + +:: + + + buf1.try_put(3); + + +``buf1`` attempts to forward 3 to ``jn``. ``jn`` does not accept the +value and the arc from ``buf1`` to ``jn`` reverses. Because not all the +inputs to ``jn`` have available predecessors, ``jn`` does nothing +further. + + +.. container:: fignone + :name: reserve_step3 + + + .. container:: imagecenter + + + |image2| + + +:: + + + buf2.try_put(4); + + +``buf2`` attempts to forward 4 to ``jn``. ``jn`` does not accept the +value and the arc from ``buf2`` to ``jn`` reverses. Now both inputs of +``jn`` have predecessors, a task to build and forward a message from +``jn`` will be spawned. We assume that task is not yet executing. + + +.. container:: fignone + :name: reserve_step4 + + + .. container:: imagecenter + + + |image3| + + +:: + + + buf2.try_put(7); + + +``buf2`` has no successor (because the arc to ``jn`` is reversed,) so it +stores the value 7. + + +.. container:: fignone + :name: reserve_step5 + + + .. container:: imagecenter + + + |image4| + + +Now the task spawned to run ``jn`` runs. + + +- ``jn`` tries to reserve ``bn``, which fails. The arc to ``bn`` + switches back to the forward direction. +- ``jn`` tries to reserve ``buf1``, which succeeds (reserved nodes are + colored grey.) ``jn`` receives the value 3 from ``buf1``, but it + remains in ``buf1`` (in case the attempt to forward a message from + ``jn`` fails.) +- ``jn`` tries to reserve ``buf2``, which succeeds. ``jn`` receives the + value 4 from ``buf2``, but it remains in ``buf2``. +- ``jn`` constructs the output message ``tuple<3,4>``. + + +.. container:: fignone + :name: reserve_step6 + + + .. container:: imagecenter + + + |image5| + + +Now ``jn`` pushes its message to ``buf_out``, which accepts it. Because +the push succeeded, ``jn`` signals ``buf1`` and ``buf2`` that the +reserved values were used, and the buffers discard those values. Now +``jn`` attempts to reserve again. + + +- No attempt to pull from ``bn`` is made, because the edge from ``bn`` + to ``jn`` is in push state. +- ``jn`` tries to reserve ``buf1``, which fails. The arc to ``buf1`` + switches back to the forward direction. +- ``jn`` does not try any further actions. + + +.. container:: fignone + :name: reserve_step7 + + + .. container:: imagecenter + + + |image6| + + +No further activity occurs in the graph, and the ``wait_for_all()`` will +complete. The output of this code is + + +:: + + + join_node output == (3,4) + buf1 was empty + buf2 had 7 + + +.. |image0| image:: Images/flow_graph_reserve_buffers_1.png + :width: 400px + :height: 222px +.. |image1| image:: Images/flow_graph_reserve_buffers_2.png + :width: 400px + :height: 222px +.. |image2| image:: Images/flow_graph_reserve_buffers_3.png + :width: 400px + :height: 222px +.. |image3| image:: Images/flow_graph_reserve_buffers_4.png + :width: 400px + :height: 222px +.. |image4| image:: Images/flow_graph_reserve_buffers_5.png + :width: 400px + :height: 222px +.. |image5| image:: Images/flow_graph_reserve_buffers_6.png + :width: 400px + :height: 222px +.. |image6| image:: Images/flow_graph_reserve_buffers_7.png + :width: 400px + :height: 222px + diff --git a/_sources/main/tbb_userguide/Flow_Graph_Single_Vs_Broadcast.rst b/_sources/main/tbb_userguide/Flow_Graph_Single_Vs_Broadcast.rst new file mode 100644 index 0000000000..23d5cd9c39 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_Single_Vs_Broadcast.rst @@ -0,0 +1,112 @@ +.. _Flow_Graph_Single_Vs_Broadcast: + +Flow Graph Basics: Single-push vs. Broadcast-push +================================================= + + +Nodes in the |full_name| flow graph +communicate by pushing and pulling messages. Two policies for pushing +messages are used, depending on the type of the node: + + +- **single-push**: No matter how many successors to the node exist and + are able to accept a message, each message will be only sent to one + successor. +- **broadcast-push**: A message will be pushed to every successor which + is connected to the node by an edge in push mode, and which accepts + the message. + + +The following code demonstrates this difference: + + +:: + + + using namespace oneapi::tbb::flow; + + + std::atomic g_cnt; + + + struct fn_body1 { + std::atomic &body_cnt; + fn_body1(std::atomic &b_cnt) : body_cnt(b_cnt) {} + continue_msg operator()( continue_msg /*dont_care*/) { + ++g_cnt; + ++body_cnt; + return continue_msg(); + } + }; + + + void run_example1() { // example for Flow_Graph_Single_Vs_Broadcast.xml + graph g; + std::atomic b1; // local counts + std::atomic b2; // for each function _node body + std::atomic b3; // + function_node f1(g,serial,fn_body1(b1)); + function_node f2(g,serial,fn_body1(b2)); + function_node f3(g,serial,fn_body1(b3)); + buffer_node buf1(g); + // + // single-push policy + // + g_cnt = b1 = b2 = b3 = 0; + make_edge(buf1,f1); + make_edge(buf1,f2); + make_edge(buf1,f3); + buf1.try_put(continue_msg()); + buf1.try_put(continue_msg()); + buf1.try_put(continue_msg()); + g.wait_for_all(); + printf( "after single-push test, g_cnt == %d, b1==%d, b2==%d, b3==%d\n", (int)g_cnt, (int)b1, (int)b2, (int)b3); + remove_edge(buf1,f1); + remove_edge(buf1,f2); + remove_edge(buf1,f3); + // + // broadcast-push policy + // + broadcast_node bn(g); + g_cnt = b1 = b2 = b3 = 0; + make_edge(bn,f1); + make_edge(bn,f2); + make_edge(bn,f3); + bn.try_put(continue_msg()); + bn.try_put(continue_msg()); + bn.try_put(continue_msg()); + g.wait_for_all(); + printf( "after broadcast-push test, g_cnt == %d, b1==%d, b2==%d, b3==%d\n", (int)g_cnt, (int)b1, (int)b2, (int)b3); + } + + +The output of this code is + + +:: + + + after single-push test, g_cnt == 3, b1==3, b2==0, b3==0 + after broadcast-push test, g_cnt == 9, b1==3, b2==3, b3==3 + + +The single-push test uses a ``buffer_node``, which has a "single-push" +policy for forwarding messages. Putting three messages to the +``buffer_node`` results in three messages being pushed. Notice also only +the first ``function_node`` is sent to; in general there is no policy +for which node is pushed to if more than one successor can accept. + + +The broadcast-push test uses a ``broadcast_node``, which will push any +message it receives to all accepting successors. Putting three messages +to the ``broadcast_node`` results in a total of nine messages pushed to +the ``function_nodes``. + + +Only nodes designed to buffer (hold and forward received messages) have +a "single-push" policy; all other nodes have a "broadcast-push" policy. + +Please see the :ref:`broadcast_or_send` section of +:ref:`Flow_Graph_Tips`, and :ref:`Flow_Graph_Buffering_in_Nodes` for more +information. + diff --git a/_sources/main/tbb_userguide/Flow_Graph_Tips.rst b/_sources/main/tbb_userguide/Flow_Graph_Tips.rst new file mode 100644 index 0000000000..7cda5022c8 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_Tips.rst @@ -0,0 +1,13 @@ +.. _Flow_Graph_Tips: + +Flow Graph Tips and Tricks +========================== + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Flow-Graph-waiting-tips + ../tbb_userguide/Flow_Graph_making_edges_tips + ../tbb_userguide/Flow_Graph_nested_parallelism_tips + ../tbb_userguide/Flow_Graph_resource_tips + ../tbb_userguide/Flow-Graph-exception-tips \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Flow_Graph_making_edges_tips.rst b/_sources/main/tbb_userguide/Flow_Graph_making_edges_tips.rst new file mode 100644 index 0000000000..49312b0432 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_making_edges_tips.rst @@ -0,0 +1,13 @@ +.. _Flow_Graph_making_edges_tips: + +Flow Graph Tips on Making Edges +=============================== + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/use_make_edge + ../tbb_userguide/broadcast_or_send + ../tbb_userguide/communicate_with_nodes + ../tbb_userguide/use_input_node + ../tbb_userguide/avoiding_data_races diff --git a/_sources/main/tbb_userguide/Flow_Graph_nested_parallelism_tips.rst b/_sources/main/tbb_userguide/Flow_Graph_nested_parallelism_tips.rst new file mode 100644 index 0000000000..cc6824bc60 --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_nested_parallelism_tips.rst @@ -0,0 +1,10 @@ +.. _Flow_Graph_nested_parallelism_tips: + +Flow Graph Tips on Nested Parallelism +===================================== + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/use_nested_algorithms + ../tbb_userguide/use_nested_flow_graphs \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Flow_Graph_resource_tips.rst b/_sources/main/tbb_userguide/Flow_Graph_resource_tips.rst new file mode 100644 index 0000000000..27dd524c5b --- /dev/null +++ b/_sources/main/tbb_userguide/Flow_Graph_resource_tips.rst @@ -0,0 +1,18 @@ +.. _Flow_Graph_resource_tips: + +Flow Graph Tips for Limiting Resource Consumption +================================================= + + +You may want to control the number of messages allowed to enter parts of +your graph, or control the maximum number of tasks in the work pool. +There are several mechanisms available for limiting resource consumption +in a flow graph. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/use_limiter_node + ../tbb_userguide/use_concurrency_limits + ../tbb_userguide/create_token_based_system + ../tbb_userguide/attach_flow_graph_to_arena \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Graph_Main_Categories.rst b/_sources/main/tbb_userguide/Graph_Main_Categories.rst new file mode 100644 index 0000000000..9f9d640161 --- /dev/null +++ b/_sources/main/tbb_userguide/Graph_Main_Categories.rst @@ -0,0 +1,21 @@ +.. _Graph_Main_Categories: + +Graph Application Categories +============================ + + +Most flow graphs fall into one of two categories: + + +- **Data flow graphs.** In this type of graph, data is passed along the + graph's edges. The nodes receive, transform and then pass along the + data messages. +- **Dependence graphs.** In this type of graph, the data operated on by + the nodes is obtained through shared memory directly and is not + passed along the edges. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Data_Flow_Graph + ../tbb_userguide/Dependence_Graph \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Graph_Object.rst b/_sources/main/tbb_userguide/Graph_Object.rst new file mode 100644 index 0000000000..06fd53312a --- /dev/null +++ b/_sources/main/tbb_userguide/Graph_Object.rst @@ -0,0 +1,34 @@ +.. _Graph_Object: + +Flow Graph Basics: Graph Object +=============================== + + +Conceptually a flow graph is a collection of nodes and edges. Each node +belongs to exactly one graph and edges are made only between nodes in +the same graph. In the flow graph interface, a graph object represents +this collection of nodes and edges, and is used for invoking whole graph +operations such as waiting for all tasks related to the graph to +complete, resetting the state of all nodes in the graph, and canceling +the execution of all nodes in the graph. + + +The code below creates a graph object and then waits for all tasks +spawned by the graph to complete. The call to ``wait_for_all`` in this +example returns immediately since this is a trivial graph with no nodes +or edges, and therefore no tasks are spawned. + + +:: + + + graph g; + g.wait_for_all(); + +The graph object does not own the nodes associated with it. You need to make sure that the graph object's lifetime is longer than the lifetimes of all nodes added to the graph and any activity associated with the graph. + +.. tip:: Call ``wait_for_all`` on a graph object before destroying it to make sure all activities are complete. + + Even when using smart pointers, be aware of the order of destruction for nodes and the graph to make sure that nodes are not deleted before the graph. + + diff --git a/_sources/main/tbb_userguide/Guiding_Task_Scheduler_Execution.rst b/_sources/main/tbb_userguide/Guiding_Task_Scheduler_Execution.rst new file mode 100644 index 0000000000..b0b33fe498 --- /dev/null +++ b/_sources/main/tbb_userguide/Guiding_Task_Scheduler_Execution.rst @@ -0,0 +1,112 @@ +.. _guiding_task_scheduler_execution: + +Guiding Task Scheduler Execution +================================ + +By default, the task scheduler tries to use all available computing resources. In some cases, +you may want to configure the task scheduler to use only some of them. + +.. caution:: + + Guiding the execution of the task scheduler may cause composability issues. + +|full_name| provides the ``task_arena`` interface to guide tasks execution within the arena by: + - setting the preferred computation units; + - restricting part of computation units. + +Such customizations are encapsulated within the ``task_arena::constraints`` structure. +To set the limitation, you have to customize the ``task_arena::constraints`` and then pass +it to the ``task_arena`` instance during the construction or initialization. + +The structure ``task_arena::constraints`` allows to specify the following restrictions: + +- Preferred NUMA node +- Preferred core type +- The maximum number of logical threads scheduled per single core simultaneously +- The level of ``task_arena`` concurrency + +You may use the interfaces from ``tbb::info`` namespace to construct the ``tbb::task_arena::constraints`` +instance. Interfaces from ``tbb::info`` namespace respect the process affinity mask. For instance, +if the process affinity mask excludes execution on some of the NUMA nodes, then these NUMA nodes are +not returned by ``tbb::info::numa_nodes()`` interface. + +The following examples show how to use these interfaces: + +.. rubric:: Setting the preferred NUMA node + +The execution on systems with non-uniform memory access (`NUMA `_ systems) +may cause a performance penalty if threads from one NUMA node access the memory allocated on +a different NUMA node. To reduce this overhead, the work may be divided among several ``task_arena`` +instances, whose execution preference is set to different NUMA nodes. To set execution preference, +assign a NUMA node identifier to the ``task_arena::constraints::numa_id`` field. + +:: + + std::vector numa_indexes = tbb::info::numa_nodes(); + std::vector arenas(numa_indexes.size()); + std::vector task_groups(numa_indexes.size()); + + for(unsigned j = 0; j < numa_indexes.size(); j++) { + arenas[j].initialize(tbb::task_arena::constraints(numa_indexes[j])); + arenas[j].execute([&task_groups, &j](){  + task_groups[j].run([](){/*some parallel stuff*/}); + }); + } + + for(unsigned j = 0; j < numa_indexes.size(); j++) { + arenas[j].execute([&task_groups, &j](){ task_groups[j].wait(); }); + } + +.. rubric:: Setting the preferred core type + +The processors with `Intel® Hybrid Technology `_ +contain several core types, each is suited for different purposes. +In most cases, systems with hybrid CPU architecture show reasonable performance without involving additional API calls. +However, in some exceptional scenarios, performance may be tuned by setting the preferred core type. +To set the preferred core type for the execution, assign a specific core type identifier to the ``task_arena::constraints::core_type`` field. + +The example shows how to set the most performant core type as preferable for work execution: + +:: + + std::vector core_types = tbb::info::core_types(); + tbb::task_arena arena( + tbb::task_arena::constraints{}.set_core_type(core_types.back()) + ); + + arena.execute( [] { + /*the most performant core type is defined as preferred.*/ + }); + +.. rubric:: Limiting the maximum number of threads simultaneously scheduled to one core + +The processors with `Intel® Hyper-Threading Technology `_ +allow more than one thread to run on each core simultaneously. However, there might be situations +when there is need to lower the number of simultaneously running threads per core. In such cases, +assign the desired value to the ``task_arena::constraints::max_threads_per_core`` field. + +The example shows how to allow only one thread to run on each core at a time: + +:: + + tbb::task_arena no_ht_arena( tbb::task_arena::constraints{}.set_max_threads_per_core(1) ); + no_ht_arena.execute( [] { + /*parallel work*/ + }); + +A more composable way to limit the number of threads executing on cores is by setting the maximal +concurrency of the ``tbb::task_arena``: + +:: + + int no_ht_concurrency = tbb::info::default_concurrency( + tbb::task_arena::constraints{}.set_max_threads_per_core(1) + ); + tbb::task_arena arena( no_ht_concurrency ); + arena.execute( [] { + /*parallel work*/ + }); + +Similarly to the previous example, the number of threads inside the arena is equal to the +number of available cores. However, this one results in fewer overheads and better composability +by imposing a less constrained execution. diff --git a/_sources/main/tbb_userguide/How_Task_Scheduler_Works.rst b/_sources/main/tbb_userguide/How_Task_Scheduler_Works.rst new file mode 100644 index 0000000000..744794fc07 --- /dev/null +++ b/_sources/main/tbb_userguide/How_Task_Scheduler_Works.rst @@ -0,0 +1,50 @@ +.. _How_Task_Scheduler_Works.rst: + +How Task Scheduler Works +======================== + + +While the task scheduler is not bound to any particular type of parallelism, +it was designed to work efficiently for fork-join parallelism with lots of forks. +This type of parallelism is typical for parallel algorithms such as `oneapi::tbb::parallel_for +`_. + +Let's consider the mapping of fork-join parallelism on the task scheduler in more detail. + +The scheduler runs tasks in a way that tries to achieve several targets simultaneously: + - Enable as many threads as possible, by creating enough job, to achieve actual parallelism + - Preserve data locality to make a single thread execution more efficient + - Minimize both memory demands and cross-thread communication to reduce an overhead + +To achieve this, a balance between depth-first and breadth-first execution strategies +must be reached. Assuming that the task graph is finite, depth-first is better for +a sequential execution because: + +- **Strike when the cache is hot**. The deepest tasks are the most recently created tasks and therefore are the hottest in the cache. + Also, if they can be completed, tasks that depend on it can continue executing, and though not the hottest in a cache, + they are still warmer than the older tasks deeper in the dequeue. + +- **Minimize space**. Execution of the shallowest task leads to the breadth-first unfolding of a graph. It creates an exponential + number of nodes that co-exist simultaneously. In contrast, depth-first execution creates the same number + of nodes, but only a linear number can exists at the same time, since it creates a stack of other ready + tasks. + +Each thread has its deque of tasks that are ready to run. When a +thread spawns a task, it pushes it onto the bottom of its deque. + +When a thread participates in the evaluation of tasks, it constantly executes +a task obtained by the first rule that applies from the roughly equivalent ruleset: + +- Get the task returned by the previous one, if any. + +- Take a task from the bottom of its deque, if any. + +- Steal a task from the top of another randomly chosen deque. If the + selected deque is empty, the thread tries again to execute this rule until it succeeds. + +Rule 1 is described in :doc:`Task Scheduler Bypass `. +The overall effect of rule 2 is to execute the *youngest* task spawned by the thread, +which causes the depth-first execution until the thread runs out of work. +Then rule 3 applies. It steals the *oldest* task spawned by another thread, +which causes temporary breadth-first execution that converts potential parallelism +into actual parallelism. diff --git a/_sources/main/tbb_userguide/Initializing_and_Terminating_the_Library.rst b/_sources/main/tbb_userguide/Initializing_and_Terminating_the_Library.rst new file mode 100644 index 0000000000..e101a068aa --- /dev/null +++ b/_sources/main/tbb_userguide/Initializing_and_Terminating_the_Library.rst @@ -0,0 +1,22 @@ +.. _Initializing_and_Terminating_the_Library: + +Initializing and Terminating the Library +======================================== + +|full_name| automatically initializes the task scheduler. +The initialization process is involved when a thread uses task scheduling services the first time, +for example any parallel algorithm, flow graph or task group. +The termination happens when the last such thread exits. + +Explicit Library Finalization +***************************** + +oneTBB supports an explicit library termination as a preview feature. +The ``oneapi::tbb::finalize`` function called with an instance of class ``oneapi::tbb::task_scheduler_handle`` blocks the calling thread +until all worker threads implicitly created by the library have completed. +If waiting for thread completion is not safe, e.g. may result in a deadlock +or called inside a task, a parallel algorithm, or a flow graph node, the method fails. + +If you know how many active ``oneapi::tbb::task_scheduler_handle`` instances exist in the program, +it is recommended to call ``oneapi::tbb::release`` function on all but the last one, then call ``oneapi::tbb::finalize`` for the last instance. + diff --git a/_sources/main/tbb_userguide/Iterating_Over_a_Concurrent_Queue_for_Debugging.rst b/_sources/main/tbb_userguide/Iterating_Over_a_Concurrent_Queue_for_Debugging.rst new file mode 100644 index 0000000000..8e88c125e5 --- /dev/null +++ b/_sources/main/tbb_userguide/Iterating_Over_a_Concurrent_Queue_for_Debugging.rst @@ -0,0 +1,29 @@ +.. _Iterating_Over_a_Concurrent_Queue_for_Debugging: + +Iterating Over a Concurrent Queue for Debugging +=============================================== + + +The template classes ``concurrent_queue`` and +``concurrent_bounded_queue`` support STL-style iteration. This support +is intended only for debugging, when you need to dump a queue. The +iterators go forwards only, and are too slow to be very useful in +production code. If a queue is modified, all iterators pointing to it +become invalid and unsafe to use. The following snippet dumps a queue. +The ``operator<<`` is defined for a ``Foo``. + + +:: + + + concurrent_queue q; + ... + typedef concurrent_queue::const_iterator iter; + for(iter i(q.unsafe_begin()); i!=q.unsafe_end(); ++i ) { + cout << *i; + } + + +The prefix ``unsafe_`` on the methods is a reminder that they are not +concurrency safe. + diff --git a/_sources/main/tbb_userguide/Lambda_Expressions.rst b/_sources/main/tbb_userguide/Lambda_Expressions.rst new file mode 100644 index 0000000000..e2408c1e0f --- /dev/null +++ b/_sources/main/tbb_userguide/Lambda_Expressions.rst @@ -0,0 +1,102 @@ +.. _Lambda_Expressions: + +Lambda Expressions +================== + + +C++11 lambda expressions make the |full_name| +``parallel_for`` much easier to use. A lambda expression lets +the compiler do the tedious work of creating a function object. + + +Below is the example from the previous section, rewritten with a lambda +expression. The lambda expression, replaces both the declaration and construction of function object ``ApplyFoo`` in the +example of the previous section. + + +:: + + + #include "oneapi/tbb.h" +   + + using namespace oneapi::tbb; +   + + void ParallelApplyFoo( float* a, size_t n ) { + parallel_for( blocked_range(0,n), + [=](const blocked_range& r) { + for(size_t i=r.begin(); i!=r.end(); ++i) + Foo(a[i]); + } + ); + } + + +The [=] introduces the lambda expression. The expression creates a +function object very similar to ``ApplyFoo``. When local variables like +``a`` and ``n`` are declared outside the lambda expression, but used +inside it, they are "captured" as fields inside the function object. The +[=] specifies that capture is by value. Writing [&] instead would +capture the values by reference. After the [=] is the parameter list and +definition for the ``operator()`` of the generated function object. The +compiler documentation says more about lambda expressions and other +implemented C++11 features. It is worth reading more complete +descriptions of lambda expressions than can fit here, because lambda +expressions are a powerful feature for using template libraries in +general. + + +C++11 support is off by default in the compiler. The following table +shows the option for turning it on. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Environment + - Intel® C++ Compiler Classic + - Intel® oneAPI DPC++/C++ Compiler + * - Windows\* OS systems + - \ ``icl /Qstd=c++11 foo.cpp`` + - \ ``icx /Qstd=c++11 foo.cpp`` + * - Linux\* OS systems + - \ ``icc -std=c++11 foo.cpp`` + - \ ``icx -std=c++11 foo.cpp`` + + + + +For further compactness, oneTBB has a form of ``parallel_for`` expressly +for parallel looping over a consecutive range of integers. The +expression ``parallel_for(first,last,step,f)`` is like writing +``for(auto i=first; i* to indicate the top-level +installation directory. The following table describes the subdirectory +structure for Linux\*, relative to ** + +.. container:: tablenoborder + + .. list-table:: + :header-rows: 1 + + * - Item + - Location + - Environment Variable + * - Header files + - | ``include/oneapi/tbb.h`` + | ``include/oneapi/tbb/*.h`` + - ``CPATH`` + * - Shared libraries + - ``lib//.so.`` + - | ``LIBRARY_PATH`` + | ``LD_LIBRARY_PATH`` + +Where: + +* ```` - ``ia32`` or ``intel64`` + + .. note:: Starting with oneTBB 2022.0, 32-bit binaries are supported only by the open-source version of the library. + +* ```` - ``libtbb``, ``libtbbmalloc``, ``libtbbmalloc_proxy`` or ``libtbbbind`` +* ```` - ``_debug`` or empty +* ```` - binary version in a form of ``.`` diff --git a/_sources/main/tbb_userguide/Lock_Pathologies.rst b/_sources/main/tbb_userguide/Lock_Pathologies.rst new file mode 100644 index 0000000000..eeff177a6c --- /dev/null +++ b/_sources/main/tbb_userguide/Lock_Pathologies.rst @@ -0,0 +1,75 @@ +.. _Lock_Pathologies: + +Lock Pathologies +================ + + +Locks can introduce performance and correctness problems. If you are new +to locking, here are some of the problems to avoid: + + +.. container:: section + + + .. rubric:: Deadlock + :class: sectiontitle + + Deadlock happens when threads are trying to acquire more than one + lock, and each holds some of the locks the other threads need to + proceed. More precisely, deadlock happens when: + + + - There is a cycle of threads + + + - Each thread holds at least one lock on a mutex, and is waiting on + a mutex for which the *next* thread in the cycle already has a + lock. + + + - No thread is willing to give up its lock. + + + Think of classic gridlock at an intersection – each car has + "acquired" part of the road, but needs to "acquire" the road under + another car to get through. Two common ways to avoid deadlock are: + + + - Avoid needing to hold two locks at the same time. Break your + program into small actions in which each can be accomplished while + holding a single lock. + + + - Always acquire locks in the same order. For example, if you have + "outer container" and "inner container" mutexes, and need to + acquire a lock on one of each, you could always acquire the "outer + sanctum" one first. Another example is "acquire locks in + alphabetical order" in a situation where the locks have names. Or + if the locks are unnamed, acquire locks in order of the mutex’s + numerical addresses. + + + - Use atomic operations instead of locks. + + +.. container:: section + + + .. rubric:: Convoying + :class: sectiontitle + + Another common problem with locks is *convoying*. Convoying occurs + when the operating system interrupts a thread that is holding a lock. + All other threads must wait until the interrupted thread resumes and + releases the lock. Fair mutexes can make the situation even worse, + because if a waiting thread is interrupted, all the threads behind it + must wait for it to resume. + + + To minimize convoying, try to hold the lock as briefly as possible. + Precompute whatever you can before acquiring the lock. + + + To avoid convoying, use atomic operations instead of locks where + possible. + diff --git a/_sources/main/tbb_userguide/Mac_OS.rst b/_sources/main/tbb_userguide/Mac_OS.rst new file mode 100644 index 0000000000..84a9894f64 --- /dev/null +++ b/_sources/main/tbb_userguide/Mac_OS.rst @@ -0,0 +1,32 @@ +.. _Mac_OS: + +macOS\* +======= + +This section uses ** to indicate the top-level installation directory. +The following table describes the subdirectory structure for macOS\*, relative to **. + +.. container:: tablenoborder + + .. list-table:: + :header-rows: 1 + + * - Item + - Location + - Environment Variable + * - Header files + - | ``include/oneapi/tbb.h`` + | ``include/oneapi/tbb/*.h`` + - ``CPATH`` + * - Shared libraries + - ``lib/..dylib`` + - | ``LIBRARY_PATH`` + | ``DYLD_LIBRARY_PATH`` + +where + +* ```` - ``libtbb``, ``libtbbmalloc`` or ``libtbbmalloc_proxy`` + +* ```` - ``_debug`` or empty + +* ```` - binary version in a form of ``.`` diff --git a/_sources/main/tbb_userguide/Mapping_Nodes2Tasks.rst b/_sources/main/tbb_userguide/Mapping_Nodes2Tasks.rst new file mode 100644 index 0000000000..d47ee857f0 --- /dev/null +++ b/_sources/main/tbb_userguide/Mapping_Nodes2Tasks.rst @@ -0,0 +1,57 @@ +.. _Mapping_Nodes2Tasks: + +Flow Graph Basics: Mapping Nodes to Tasks +========================================= + + +The following figure shows the timeline for one possible execution of +the two node graph example in the previous section. The bodies of n and +m will be referred to as λ\ :sub:`n` and λ\ :sub:`m`, respectively. The +three calls to try_put spawn three tasks; each one applies the lambda +expression, λ\ :sub:`n`, on one of the three input messages. Because n +has unlimited concurrency, these tasks can execute concurrently if there +are enough threads available. The call to ``g.wait_for_all()`` blocks until +there are no tasks executing in the graph. As with other ``wait_for_all`` +functions in oneTBB, the thread that calls ``wait_for_all`` is not spinning +idly during this time, but instead can join in executing other tasks +from the work pool. + + +.. container:: fignone + + + **Execution Timeline of a Two Node Graph** + + + .. container:: imagecenter + + + |image0| + + +As each task from n finishes, it puts its output to m, since m is a +successor of n. Unlike node n, m has been constructed with a concurrency +limit of 1 and therefore does not spawn all tasks immediately. Instead, +it sequentially spawns tasks to execute its body, λ\ :sub:`m`, on the +messages in the order that they arrive. When all tasks are complete, the +call to ``wait_for_all`` returns. + + +.. note:: + All execution in the flow graph happens asynchronously. The calls to + try_put return control to the calling thread quickly, after either + immediately spawning a task or buffering the message being passed. + Likewise, the body tasks execute the lambda expressions and then put + the result to any successor nodes. Only the call to ``wait_for_all`` + blocks, as it should, and even in this case the calling thread may be + used to execute tasks from the oneTBB work pool while it is waiting. + + +The above timeline shows the sequence when there are enough threads to +execute all of the tasks that can be executed in parallel. If there are +fewer threads, some spawned tasks will need to wait until a thread is +available to execute them. + + +.. |image0| image:: Images/execution_timeline2node.jpg + diff --git a/_sources/main/tbb_userguide/Memory_Allocation.rst b/_sources/main/tbb_userguide/Memory_Allocation.rst new file mode 100644 index 0000000000..745f0258db --- /dev/null +++ b/_sources/main/tbb_userguide/Memory_Allocation.rst @@ -0,0 +1,71 @@ +.. _Memory_Allocation: + +Memory Allocation +================= + + +|full_name| provides several memory +allocator templates that are similar to the STL template class +std::allocator. Two templates, ``scalable_allocator`` and +``cache_aligned_allocator``, address critical issues in parallel +programming as follows: + + +- **Scalability**. Problems of scalability arise when using memory + allocators originally designed for serial programs, on threads that + might have to compete for a single shared pool in a way that allows + only one thread to allocate at a time. + + + Use the ``scalable_allocator`` template to avoid scalability + bottlenecks. This template can improve the performance of programs + that rapidly allocate and free memory. + + +- **False sharing**. Problems of sharing arise when two threads access + different words that share the same cache line. The problem is that a + cache line is the unit of information interchange between processor + caches. If one processor modifies a cache line and another processor + reads the same cache line, the line must be moved from one processor + to the other, even if the two processors are dealing with different + words within the line. False sharing can hurt performance because + cache lines can take hundreds of clocks to move. + + + Use the ``cache_aligned_allocator`` template to always allocate on + a separate cache line. Two objects allocated by + ``cache_aligned_allocator`` are guaranteed to not have false sharing. + However, if an object is allocated by ``cache_aligned_allocator`` and + another object is allocated some other way, there is no guarantee. + + +You can use these allocator templates as the *allocator* argument to STL +template classes.The following code shows how to declare an STL vector +that uses ``cache_aligned_allocator``\ for allocation: + + +:: + + + std::vector >; + + +.. tip:: + The functionality of ``cache_aligned_allocator`` comes at some + cost in space, because it must allocate at least one cache line’s + worth of memory, even for a small object. So use + ``cache_aligned_allocator`` only if false sharing is likely to be + a real problem. + + +The scalable memory allocator also provides a set of functions +equivalent to the C standard library memory management routines but has +the ``scalable_`` prefix in their names, as well as the way to easily +redirect the standard routines to these functions. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Which_Dynamic_Libraries_to_Use + ../tbb_userguide/Allocator_Configuration + ../tbb_userguide/automatically-replacing-malloc diff --git a/_sources/main/tbb_userguide/Migration_Guide.rst b/_sources/main/tbb_userguide/Migration_Guide.rst new file mode 100644 index 0000000000..2ff3a4e293 --- /dev/null +++ b/_sources/main/tbb_userguide/Migration_Guide.rst @@ -0,0 +1,15 @@ +.. _Migration_Guide: + +Migrating from Threading Building Blocks (TBB) +============================================== + +While oneTBB is mostly source compatible with TBB, some interfaces were deprecated +in TBB and redesigned or removed in oneTBB. This section considers the most difficult use cases for +migrating TBB to oneTBB. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Migration_Guide/Task_Scheduler_Init + ../tbb_userguide/Migration_Guide/Task_API + ../tbb_userguide/Migration_Guide/Mixing_Two_Runtimes diff --git a/_sources/main/tbb_userguide/Migration_Guide/Mixing_Two_Runtimes.rst b/_sources/main/tbb_userguide/Migration_Guide/Mixing_Two_Runtimes.rst new file mode 100644 index 0000000000..8d467fb620 --- /dev/null +++ b/_sources/main/tbb_userguide/Migration_Guide/Mixing_Two_Runtimes.rst @@ -0,0 +1,49 @@ +.. _Mixing_Two_Runtimes: + +Mixing two runtimes +======================================= + +Threading Building Blocks (TBB) and oneAPI Threading Building Blocks (oneTBB) can be safely used in +the same application. TBB and oneTBB runtimes are named differently and can be loaded safely within +the same process. In addition, the ABI versioning is completely different that prevents symbols +conflicts. + +However, if both runtimes are loaded into the same process it can lead to +oversubscription because each runtime will use its own pool of threads. It might lead to a +performance penalty due to increased number of context switches. To check if both TBB and +oneTBB are loaded to the application, export ``TBB_VERSION=1`` before the application run. If +both runtimes are loaded there will be two blocks of output, for example: + +oneTBB possible output: + +.. code:: text + + oneTBB: SPECIFICATION VERSION 1.0 + oneTBB: VERSION 2021.2 + oneTBB: INTERFACE VERSION 12020 + oneTBB: TBB_USE_DEBUG 1 + oneTBB: TBB_USE_ASSERT 1 + oneTBB: TOOLS SUPPORT disabled + +TBB possible output: + +.. code:: text + + TBB: VERSION 2018.0 + TBB: INTERFACE VERSION 10006 + TBB: BUILD_DATE Mon 01 Mar 2021 01:28:40 PM UTC + TBB: BUILD_HOST localhost (x86_64) + TBB: BUILD_OS Fedora release 32 (Thirty Two) + TBB: BUILD_KERNEL Linux 5.8.9-200.fc32.x86_64 #1 SMP Mon Sep 14 18:28:45 UTC 2020 + TBB: BUILD_GCC g++ (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9) + TBB: BUILD_LIBC 2.31 + TBB: BUILD_LD GNU ld version 2.34-6.fc32 + TBB: BUILD_TARGET intel64 on cc10_libc2.31_kernel5.8.9 + TBB: BUILD_COMMAND g++ -DDO_ITT_NOTIFY -g -O2 -DUSE_PTHREAD -m64 -fPIC -D__TBB_BUILD=1 -Wall -Wno-parentheses -Wno-non-virtual-dtor -I../../src -I../../src/rml/include -I../../include -I. + TBB: TBB_USE_DEBUG 0 + TBB: TBB_USE_ASSERT 0 + TBB: DO_ITT_NOTIFY 1 + TBB: RML private + TBB: Tools support disabled + +.. note:: The ``tbbmalloc`` library in oneTBB is fully binary compatible with TBB. diff --git a/_sources/main/tbb_userguide/Migration_Guide/Task_API.rst b/_sources/main/tbb_userguide/Migration_Guide/Task_API.rst new file mode 100644 index 0000000000..e96e4324b3 --- /dev/null +++ b/_sources/main/tbb_userguide/Migration_Guide/Task_API.rst @@ -0,0 +1,453 @@ +.. _Task_API: + +Migrating from low-level task API +================================= + +The low-level task API of Intel(R) Threading Building Blocks (TBB) was considered complex and hence +error-prone, which was the primary reason it had been removed from oneAPI Threading Building Blocks +(oneTBB). This guide helps with the migration from TBB to oneTBB for the use cases where low-level +task API is used. + +Spawning of individual tasks +---------------------------- +For most use cases, the spawning of individual tasks can be replaced with the use of either +``oneapi::tbb::task_group`` or ``oneapi::tbb::parallel_invoke``. + +For example, ``RootTask``, ``ChildTask1``, and ``ChildTask2`` are the user-side functors that +inherit ``tbb::task`` and implement its interface. Then spawning of ``ChildTask1`` and +``ChildTask2`` tasks that can execute in parallel with each other and waiting on the ``RootTask`` is +implemented as: + +.. code:: cpp + + #include + + int main() { + // Assuming RootTask, ChildTask1, and ChildTask2 are defined. + RootTask& root = *new(tbb::task::allocate_root()) RootTask{}; + + ChildTask1& child1 = *new(root.allocate_child()) ChildTask1{/*params*/}; + ChildTask2& child2 = *new(root.allocate_child()) ChildTask2{/*params*/}; + + root.set_ref_count(3); + + tbb::task::spawn(child1); + tbb::task::spawn(child2); + + root.wait_for_all(); + } + + +Using ``oneapi::tbb::task_group`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The code above can be rewritten using ``oneapi::tbb::task_group``: + +.. code:: cpp + + #include + + int main() { + // Assuming ChildTask1, and ChildTask2 are defined. + oneapi::tbb::task_group tg; + tg.run(ChildTask1{/*params*/}); + tg.run(ChildTask2{/*params*/}); + tg.wait(); + } + +The code looks more concise now. It also enables lambda functions and does not require you to +implement ``tbb::task`` interface that overrides the ``tbb::task* tbb::task::execute()`` virtual +method. With this new approach, you work with functors in a C++-standard way by implementing ``void +operator() const``: + +.. code:: cpp + + struct Functor { + // Member to be called when object of this type are passed into + // oneapi::tbb::task_group::run() method + void operator()() const {} + }; + + +Using ``oneapi::tbb::parallel_invoke`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is also possible to use ``oneapi::tbb::parallel_invoke`` to rewrite the original code and make it +even more concise: + +.. code:: cpp + + #include + + int main() { + // Assuming ChildTask1, and ChildTask2 are defined. + oneapi::tbb::parallel_invoke( + ChildTask1{/*params*/}, + ChildTask2{/*params*/} + ); + } + + +Adding more work during task execution +-------------------------------------- +``oneapi::tbb::parallel_invoke`` follows a blocking style of programming, which means that it +completes only when all functors passed to the parallel pattern complete their execution. + +In TBB, cases when the amount of work is not known in advance and the work needs to be added during +the execution of a parallel algorithm were mostly covered by ``tbb::parallel_do`` high-level +parallel pattern. The ``tbb::parallel_do`` algorithm logic may be implemented using the task API as: + +.. code:: cpp + + #include + #include + #include + + // Assuming RootTask and OtherWork are defined and implement tbb::task interface. + + struct Task : public tbb::task { + Task(tbb::task& root, int i) + : m_root(root), m_i(i) + {} + + tbb::task* execute() override { + // ... do some work for item m_i ... + + if (add_more_parallel_work) { + tbb::task& child = *new(m_root.allocate_child()) OtherWork; + tbb::task::spawn(child); + } + return nullptr; + } + + tbb::task& m_root; + int m_i; + }; + + int main() { + std::vector items = { 0, 1, 2, 3, 4, 5, 6, 7 }; + RootTask& root = *new(tbb::task::allocate_root()) RootTask{/*params*/}; + + root.set_ref_count(items.size() + 1); + + for (std::size_t i = 0; i < items.size(); ++i) { + Task& task = *new(root.allocate_child()) Task(root, items[i]); + tbb::task::spawn(task); + } + + root.wait_for_all(); + return 0; + } + +In oneTBB ``tbb::parallel_do`` interface was removed. Instead, the functionality of adding new work +was included into the ``oneapi::tbb::parallel_for_each`` interface. + +The previous use case can be rewritten in oneTBB as follows: + +.. code:: cpp + + #include + #include + + int main() { + std::vector items = { 0, 1, 2, 3, 4, 5, 6, 7 }; + + oneapi::tbb::parallel_for_each( + items.begin(), items.end(), + [](int& i, tbb::feeder& feeder) { + + // ... do some work for item i ... + + if (add_more_parallel_work) + feeder.add(i); + } + ); + } + +Since both TBB and oneTBB support nested expressions, you can run additional functors from within an +already running functor. + +The previous use case can be rewritten using ``oneapi::tbb::task_group`` as: + +.. code:: cpp + + #include + #include + #include + + int main() { + std::vector items = { 0, 1, 2, 3, 4, 5, 6, 7 }; + + oneapi::tbb::task_group tg; + for (std::size_t i = 0; i < items.size(); ++i) { + tg.run([&i = items[i], &tg] { + + // ... do some work for item i ... + + if (add_more_parallel_work) + // Assuming OtherWork is defined. + tg.run(OtherWork{}); + + }); + } + tg.wait(); + } + + +Task recycling +-------------- +You can re-run the functor by passing ``*this`` to the ``oneapi::tbb::task_group::run()`` +method. The functor will be copied in this case. However, its state can be shared among instances: + +.. code:: cpp + + #include + #include + + struct SharedStateFunctor { + std::shared_ptr m_shared_data; + oneapi::tbb::task_group& m_task_group; + + void operator()() const { + // do some work processing m_shared_data + + if (has_more_work) + m_task_group.run(*this); + + // Note that this might be concurrently accessing m_shared_data already + } + }; + + int main() { + // Assuming Data is defined. + std::shared_ptr data = std::make_shared(/*params*/); + oneapi::tbb::task_group tg; + tg.run(SharedStateFunctor{data, tg}); + tg.wait(); + } + +Such patterns are particularly useful when the work within a functor is not completed but there is a +need for the task scheduler to react to outer circumstances, such as cancellation of group +execution. To avoid issues with concurrent access, it is recommended to submit it for re-execution +as the last step: + +.. code:: cpp + + #include + #include + + struct SharedStateFunctor { + std::shared_ptr m_shared_data; + oneapi::tbb::task_group& m_task_group; + + void operator()() const { + // do some work processing m_shared_data + + if (need_to_yield) { + m_task_group.run(*this); + return; + } + } + }; + + int main() { + // Assuming Data is defined. + std::shared_ptr data = std::make_shared(/*params*/); + oneapi::tbb::task_group tg; + tg.run(SharedStateFunctor{data, tg}); + tg.wait(); + } + + +Recycling as child or continuation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In oneTBB this kind of recycling is done manually. You have to track when it is time to run the +task: + +.. code:: cpp + + #include + #include + #include + #include + #include + + struct ContinuationTask { + ContinuationTask(std::vector& data, int& result) + : m_data(data), m_result(result) + {} + + void operator()() const { + for (const auto& item : m_data) + m_result += item; + } + + std::vector& m_data; + int& m_result; + }; + + struct ChildTask { + ChildTask(std::vector& data, int& result, + std::atomic& tasks_left, std::atomic& tasks_done, + oneapi::tbb::task_group& tg) + : m_data(data), m_result(result), m_tasks_left(tasks_left), m_tasks_done(tasks_done), m_tg(tg) + {} + + void operator()() const { + std::size_t index = --m_tasks_left; + m_data[index] = produce_item_for(index); + std::size_t done_num = ++m_tasks_done; + if (index % 2 != 0) { + // Recycling as child + m_tg.run(*this); + return; + } else if (done_num == m_data.size()) { + assert(m_tasks_left == 0); + // Spawning a continuation that does reduction + m_tg.run(ContinuationTask(m_data, m_result)); + } + } + std::vector& m_data; + int& m_result; + std::atomic& m_tasks_left; + std::atomic& m_tasks_done; + oneapi::tbb::task_group& m_tg; + }; + + + int main() { + int result = 0; + std::vector items(10, 0); + std::atomic tasks_left{items.size()}; + std::atomic tasks_done{0}; + + oneapi::tbb::task_group tg; + for (std::size_t i = 0; i < items.size(); i+=2) { + tg.run(ChildTask(items, result, tasks_left, tasks_done, tg)); + } + tg.wait(); + } + + +Scheduler Bypass +---------------- + +TBB ``task::execute()`` method can return a pointer to a task that can be executed next by the current thread. +This might reduce scheduling overheads compared to direct ``spawn``. Similar to ``spawn``, the returned task +is not guaranteed to be executed next by the current thread. + +.. code:: cpp + + #include + + // Assuming OtherTask is defined. + + struct Task : tbb::task { + task* execute(){ + // some work to do ... + + auto* other_p = new(this->parent().allocate_child()) OtherTask{}; + this->parent().add_ref_count(); + + return other_p; + } + }; + + int main(){ + // Assuming RootTask is defined. + RootTask& root = *new(tbb::task::allocate_root()) RootTask{}; + + Task& child = *new(root.allocate_child()) Task{/*params*/}; + + root.add_ref_count(); + + tbb::task_spawn(child); + + root.wait_for_all(); + } + +In oneTBB, this can be done using ``oneapi::tbb::task_group``. + +.. code:: cpp + + #include + + // Assuming OtherTask is defined. + + int main(){ + oneapi::tbb::task_group tg; + + tg.run([&tg](){ + //some work to do ... + + return tg.defer(OtherTask{}); + }); + + tg.wait(); + } + +Here ``oneapi::tbb::task_group::defer`` adds a new task into the ``tg``. However, the task is not put into a +queue of tasks ready for execution via ``oneapi::tbb::task_group::run``, but bypassed to the executing thread directly +via function return value. + +Deferred task creation +---------------------- +The TBB low-level task API separates the task creation from the actual spawning. This separation allows to +postpone the task spawning, while the parent task and final result production are blocked from premature leave. +For example, ``RootTask``, ``ChildTask``, and ``CallBackTask`` are the user-side functors that +inherit ``tbb::task`` and implement its interface. Then, blocking the ``RootTask`` from leaving prematurely +and waiting on it is implemented as follows: + +.. code:: cpp + + #include + + int main() { + // Assuming RootTask, ChildTask, and CallBackTask are defined. + RootTask& root = *new(tbb::task::allocate_root()) RootTask{}; + + ChildTask& child = *new(root.allocate_child()) ChildTask{/*params*/}; + CallBackTask& cb_task = *new(root.allocate_child()) CallBackTask{/*params*/}; + + root.set_ref_count(3); + + tbb::task::spawn(child); + + register_callback([cb_task&](){ + tbb::task::enqueue(cb_task); + }); + + root.wait_for_all(); + // Control flow will reach here only after both ChildTask and CallBackTask are executed, + // i.e. after the callback is called + } + +In oneTBB, this can be done using ``oneapi::tbb::task_group``. + +.. code:: cpp + + #include + + int main(){ + oneapi::tbb::task_group tg; + oneapi::tbb::task_arena arena; + // Assuming ChildTask and CallBackTask are defined. + + auto cb = tg.defer(CallBackTask{/*params*/}); + + register_callback([&tg, c = std::move(cb), &arena]{ + arena.enqueue(c); + }); + + tg.run(ChildTask{/*params*/}); + + + tg.wait(); + // Control flow gets here once both ChildTask and CallBackTask are executed + // i.e. after the callback is called + } + +Here ``oneapi::tbb::task_group::defer`` adds a new task into the ``tg``. However, the task is not spawned until +``oneapi::tbb::task_arena::enqueue`` is called. + +.. note:: + The call to ``oneapi::tbb::task_group::wait`` will not return control until both ``ChildTask`` and + ``CallBackTask`` are executed. diff --git a/_sources/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst b/_sources/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst new file mode 100644 index 0000000000..6acdb272eb --- /dev/null +++ b/_sources/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst @@ -0,0 +1,158 @@ +.. _Task_Scheduler_Init: + +Migrating from tbb::task_scheduler_init +======================================= + +``tbb::task_scheduler_init`` was a multipurpose functionality in the previous versions of Threading +Building Blocks (TBB). This section considers different use cases and how they can be covered with +oneTBB. + +Managing the number of threads +--------------------------------------- + +Querying the default number of threads +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* `oneapi::tbb::info::default_concurrency() + `_ + returns the maximum concurrency that will be created by *default* in implicit or explicit ``task_arena``. + +* `oneapi::tbb::this_task_arena::max_concurrency() + `_ + returns the maximum number of threads available for the parallel algorithms within the current context + (or *default* if an implicit arena is not initialized) + +* `oneapi::tbb::global_control::active_value(tbb::global_control::max_allowed_parallelism) + `_ + returns the current limit of the thread pool (or *default* if oneTBB scheduler is not initialized) + +Setting the maximum concurrency +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* `task_arena(/* max_concurrency */) + `_ + limits the maximum concurrency of the parallel algorithm running inside ``task_arena`` + +* `tbb::global_control(tbb::global_control::max_allowed_parallelism, /* max_concurrency */) + `_ + limits the total number of oneTBB worker threads + +Examples +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default parallelism: + +.. code:: cpp + + #include + #include + #include + #include + + int main() { + // Get the default number of threads + int num_threads = oneapi::tbb::info::default_concurrency(); + + // Run the default parallelism + oneapi::tbb::parallel_for( /* ... */ [] { + // Assert the maximum number of threads + assert(num_threads == oneapi::tbb::this_task_arena::max_concurrency()); + }); + + // Create the default task_arena + oneapi::tbb::task_arena arena; + arena.execute([]{ + oneapi::tbb::parallel_for( /* ... */ [] { + // Assert the maximum number of threads + assert(num_threads == oneapi::tbb::this_task_arena::max_concurrency()); + }); + }); + + return 0; + } + +The limited parallelism: + +.. code:: cpp + + #include + #include + #include + #include + #include + + int main() { + // Create the custom task_arena with four threads + oneapi::tbb::task_arena arena(4); + arena.execute([]{ + oneapi::tbb::parallel_for( /* ... */ [] { + // This arena is limited with for threads + assert(oneapi::tbb::this_task_arena::max_concurrency() == 4); + }); + }); + + // Limit the number of threads to two for all oneTBB parallel interfaces + oneapi::tbb::global_control global_limit(oneapi::tbb::global_control::max_allowed_parallelism, 2); + + // the default parallelism + oneapi::tbb::parallel_for( /* ... */ [] { + // No more than two threads is expected; however, tbb::this_task_arena::max_concurrency() can return a bigger value + int thread_limit = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); + assert(thread_limit == 2); + }); + + arena.execute([]{ + oneapi::tbb::parallel_for( /* ... */ [] { + // No more than two threads is expected; however, tbb::this_task_arena::max_concurrency() is four + int thread_limit = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); + assert(thread_limit == 2); + assert(tbb::this_task_arena::max_concurrency() == 4); + }); + }); + + return 0; + } + +Setting thread stack size +--------------------------------------- +Use `oneapi::tbb::global_control(oneapi::tbb::global_control::thread_stack_size, /* stack_size */) +`_ +to set the stack size for oneTBB worker threads: + +.. code:: cpp + + #include + #include + + int main() { + // Set 16 MB of the stack size for oneTBB worker threads. + // Note that the stack size of the main thread should be configured in accordace with the + // system documentation, e.g. at application startup moment + oneapi::tbb::global_control global_limit(tbb::global_control::thread_stack_size, 16 * 1024 * 1024); + + oneapi::tbb::parallel_for( /* ... */ [] { + // Create a big array in the stack + char big_array[10*1024*1024]; + }); + + return 0; + } + +Terminating oneTBB scheduler +--------------------------------------- + +`task_scheduler_handle `_ +allows waiting for oneTBB worker threads completion: + +.. code:: cpp + + #include + #include + + int main() { + oneapi::tbb::task_scheduler_handle handle{tbb::attach{}}; + // Do some parallel work here + oneapi::tbb::parallel_for(/* ... */); + oneapi::tbb::finalize(handle); + return 0; + } diff --git a/_sources/main/tbb_userguide/More_on_HashCompare.rst b/_sources/main/tbb_userguide/More_on_HashCompare.rst new file mode 100644 index 0000000000..5a9c9a725e --- /dev/null +++ b/_sources/main/tbb_userguide/More_on_HashCompare.rst @@ -0,0 +1,91 @@ +.. _More_on_HashCompare: + +More on HashCompare +=================== + + +There are several ways to make the ``HashCompare`` argument for +``concurrent_hash_map`` work for your own types. + + +- Specify the ``HashCompare`` argument explicitly + + +- Let the ``HashCompare`` default to ``tbb_hash_compare`` and do + one of the following: + + + - Define a specialization of template ``tbb_hash_compare``. + + +For example, if you have keys of type ``Foo``, and ``operator==`` is +defined for ``Foo``, you just have to provide a definition of +``tbb_hasher`` as shown below: + + +:: + + + size_t tbb_hasher(const Foo& f) { + size_t h = ...compute hash code for f... + return h; + }; + + +In general, the definition of ``tbb_hash_compare`` or +``HashCompare`` must provide two signatures: + + +- A method ``hash`` that maps a ``Key`` to a ``size_t`` + + +- A method ``equal`` that determines if two keys are equal + + +The signatures go together in a single class because *if two keys are +equal, then they must hash to the same value*, otherwise the hash table +might not work. You could trivially meet this requirement by always +hashing to ``0``, but that would cause tremendous inefficiency. Ideally, +each key should hash to a different value, or at least the probability +of two distinct keys hashing to the same value should be kept low. + + +The methods of ``HashCompare`` should be ``static`` unless you need to +have them behave differently for different instances. If so, then you +should construct the ``concurrent_hash_map`` using the constructor that +takes a ``HashCompare`` as a parameter. The following example is a +variation on an earlier example with instance-dependent methods. The +instance performs both case-sensitive or case-insensitive hashing, and +comparison, depending upon an internal flag ``ignore_case``. + + +:: + + + // Structure that defines hashing and comparison operations + class VariantHashCompare { + // If true, then case of letters is ignored. + bool ignore_case; + public: + size_t hash(const string& x) const { + size_t h = 0; + for(const char* s = x.c_str(); *s; s++) + h = (h*16777179)^*(ignore_case?tolower(*s):*s); + return h; + } + // True if strings are equal + bool equal(const string& x, const string& y) const { + if( ignore_case ) + strcasecmp(x.c_str(), y.c_str())==0; + else + return x==y; + } + VariantHashCompare(bool ignore_case_) : ignore_case(ignore_case_) {} + }; +   + + typedef concurrent_hash_map VariantStringTable; +   + + VariantStringTable CaseSensitiveTable(VariantHashCompare(false)); + VariantStringTable CaseInsensitiveTable(VariantHashCompare(true)); diff --git a/_sources/main/tbb_userguide/Mutex_Flavors.rst b/_sources/main/tbb_userguide/Mutex_Flavors.rst new file mode 100644 index 0000000000..3eaa79f0b5 --- /dev/null +++ b/_sources/main/tbb_userguide/Mutex_Flavors.rst @@ -0,0 +1,176 @@ +.. _Mutex_Flavors: + +Mutex Flavors +============= + + +Connoisseurs of mutexes distinguish various attributes of mutexes. It +helps to know some of these, because they involve tradeoffs of +generality and efficiency. Picking the right one often helps +performance. Mutexes can be described by the following qualities, also +summarized in the table below. + + +- **Scalable**. Some mutexes are called *scalable*. In a strict sense, + this is not an accurate name, because a mutex limits execution to one + thread at a time. A *scalable mutex* is one that does not do *worse* + than this. A mutex can do worse than serialize execution if the + waiting threads consume excessive processor cycles and memory + bandwidth, reducing the speed of threads trying to do real work. + Scalable mutexes are often slower than non-scalable mutexes under + light contention, so a non-scalable mutex may be better. When in + doubt, use a scalable mutex. + + +- **Fair**. Mutexes can be *fair* or *unfair*. A fair mutex lets + threads through in the order they arrived. Fair mutexes avoid + starving threads. Each thread gets its turn. However, unfair mutexes + can be faster, because they let threads that are running go through + first, instead of the thread that is next in line which may be + sleeping on account of an interrupt. + + +- **Yield or Block**. This is an implementation detail that impacts + performance. On long waits, an |full_name| + mutex either *yields* or *blocks*. Here *yields* means to + repeatedly poll whether progress can be made, and if not, temporarily + yield [#]_ the processor. To *block* means to yield the + processor until the mutex permits progress. Use the yielding mutexes + if waits are typically short and blocking mutexes if waits are + typically long. + + +The following is a summary of mutex behaviors: + + +- ``spin_mutex`` is non-scalable, unfair, non-recursive, and spins in + user space. It would seem to be the worst of all possible worlds, + except that it is *very fast* in *lightly contended* situations. If + you can design your program so that contention is somehow spread out + among many ``spin_mutex`` objects, you can improve performance over + using other kinds of mutexes. If a mutex is heavily contended, your + algorithm will not scale anyway. Consider redesigning the algorithm + instead of looking for a more efficient lock. + + +- ``mutex`` has behavior similar to the ``spin_mutex``. However, + the ``mutex`` *blocks* on long waits that makes it + resistant to high contention. + + +- ``queuing_mutex`` is scalable, fair, non-recursive, and spins in user + space. Use it when scalability and fairness are important. + + +- ``spin_rw_mutex`` and ``queuing_rw_mutex`` are similar to + ``spin_mutex`` and ``queuing_mutex``, but additionally support + *reader* locks. + + +- ``rw_mutex`` is similar to ``mutex``, but additionally support + *reader* locks. + + +- ``speculative_spin_mutex`` and ``speculative_spin_rw_mutex`` are + similar to ``spin_mutex`` and ``spin_rw_mutex``, but additionally + provide *speculative locking* on processors that support hardware + transaction memory. Speculative locking allows multiple threads + acquire the same lock, as long as there are no "conflicts" that may + generate different results than non-speculative locking. These + mutexes are *scalable* when work with low conflict rate, i.e. mostly + in speculative locking mode. + + +- ``null_mutex`` and ``null_rw_mutex`` do nothing. They can be useful + as template arguments. For example, suppose you are defining a + container template and know that some instantiations will be shared + by multiple threads and need internal locking, but others will be + private to a thread and not need locking. You can define the template + to take a Mutex type parameter. The parameter can be one of the real + mutex types when locking is necessary, and ``null_mutex`` when + locking is unnecessary. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Mutex + - Scalable + - Fair + - Recursive + - Long Wait + - Size + * - \ ``spin_mutex`` + - no + - no + - no + - yields + - 1 byte + * - \ ``mutex`` + - ✓ + - no + - no + - blocks + - 1 byte + * - \ ``speculative_spin_mutex`` + - HW dependent + - no + - no + - yields + - 2 cache lines + * - \ ``queuing_mutex`` + - ✓ + - ✓ + - no + - yields + - 1 word + * - \ ``spin_rw_mutex`` + - no + - no + - no + - yields + - 1 word + * - \ ``spin_rw_mutex`` + - ✓ + - no + - no + - blocks + - 1 word + * - \ ``speculative_spin_rw_mutex`` + - HW dependent + - no + - no + - yields + - 3 cache lines + * - \ ``queuing_rw_mutex`` + - ✓ + - ✓ + - no + - yields + - 1 word + * - \ ``null_mutex`` [#]_ + - moot + - ✓ + - ✓ + - never + - empty + * - \ ``null_rw_mutex`` + - moot + - ✓ + - ✓ + - never + - empty + + + + +.. [#] The yielding is implemented via ``SwitchToThread()`` on Microsoft + Windows\* operating systems and by ``sched_yield()`` on other systems. + + +.. [#] Null mutexes are considered fair by oneTBB because they cannot cause + starvation. They lack any non-static data members. + diff --git a/_sources/main/tbb_userguide/Mutual_Exclusion.rst b/_sources/main/tbb_userguide/Mutual_Exclusion.rst new file mode 100644 index 0000000000..76ca8f9aa0 --- /dev/null +++ b/_sources/main/tbb_userguide/Mutual_Exclusion.rst @@ -0,0 +1,130 @@ +.. _Mutual_Exclusion: + +Mutual Exclusion +================ + + +Mutual exclusion controls how many threads can simultaneously run a +region of code. In |full_name|, mutual +exclusion is implemented by *mutexes* and *locks.* A mutex is an object +on which a thread can acquire a lock. Only one thread at a time can have +a lock on a mutex; other threads have to wait their turn. + + +The simplest mutex is ``spin_mutex``. A thread trying to acquire a lock +on a ``spin_mutex`` busy waits until it can acquire the lock. A +``spin_mutex`` is appropriate when the lock is held for only a few +instructions. For example, the following code uses a mutex +``FreeListMutex`` to protect a shared variable ``FreeList``. It checks +that only a single thread has access to ``FreeList`` at a time. + +:: + + Node* FreeList; + typedef spin_mutex FreeListMutexType; + FreeListMutexType FreeListMutex; +   + + Node* AllocateNode() { + Node* n; + { + FreeListMutexType::scoped_lock lock(FreeListMutex); + n = FreeList; + if( n ) + FreeList = n->next; + } + if( !n ) + n = new Node(); + return n; + } +   + + void FreeNode( Node* n ) { + FreeListMutexType::scoped_lock lock(FreeListMutex); + n->next = FreeList; + FreeList = n; + } + + +The constructor for ``scoped_lock`` waits until there are no other locks +on ``FreeListMutex``. The destructor releases the lock. The braces +inside routine ``AllocateNode`` may look unusual. Their role is to keep +the lifetime of the lock as short as possible, so that other waiting +threads can get their chance as soon as possible. + + +.. CAUTION:: + Be sure to name the lock object, otherwise it will be destroyed too + soon. For example, if the creation of the ``scoped_lock`` object in + the example is changed to + + :: + + FreeListMutexType::scoped_lock (FreeListMutex); + + then the ``scoped_lock`` is destroyed when execution reaches the + semicolon, which releases the lock *before* ``FreeList`` is accessed. + + +The following shows an alternative way to write ``AllocateNode``: + + +:: + + + Node* AllocateNode() { + Node* n; + FreeListMutexType::scoped_lock lock; + lock.acquire(FreeListMutex); + n = FreeList; + if( n ) + FreeList = n->next; + lock.release(); + if( !n ) + n = new Node(); + return n; + } + + +Method ``acquire`` waits until it can acquire a lock on the mutex; +method ``release`` releases the lock. + + +It is recommended that you add extra braces where possible, to clarify +to maintainers which code is protected by the lock. + + +If you are familiar with C interfaces for locks, you may be wondering +why there are not simply acquire and release methods on the mutex object +itself. The reason is that the C interface would not be exception safe, +because if the protected region threw an exception, control would skip +over the release. With the object-oriented interface, destruction of the +``scoped_lock`` object causes the lock to be released, no matter whether +the protected region was exited by normal control flow or an exception. +This is true even for our version of ``AllocateNode`` that used methods +``acquire`` and ``release –`` the explicit release causes the lock to be +released earlier, and the destructor then sees that the lock was +released and does nothing. + + +All mutexes in oneTBB have a similar interface, which not only makes +them easier to learn, but enables generic programming. For example, all +of the mutexes have a nested ``scoped_lock`` type, so given a mutex of +type ``M``, the corresponding lock type is ``M::scoped_lock``. + + +.. tip:: + It is recommended that you always use a ``typedef`` for the mutex + type, as shown in the previous examples. That way, you can change the + type of the lock later without having to edit the rest of the code. + In the examples, you could replace the ``typedef`` with + ``typedef queuing_mutex FreeListMutexType``, and the code would still + be correct. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Mutex_Flavors + ../tbb_userguide/Reader_Writer_Mutexes + ../tbb_userguide/UpgradeDowngrade + ../tbb_userguide/Lock_Pathologies diff --git a/_sources/main/tbb_userguide/Nodes.rst b/_sources/main/tbb_userguide/Nodes.rst new file mode 100644 index 0000000000..1e8333bbbb --- /dev/null +++ b/_sources/main/tbb_userguide/Nodes.rst @@ -0,0 +1,151 @@ +.. _Nodes: + +Flow Graph Basics: Nodes +======================== + + +A node is a class that inherits from oneapi::tbb::flow::graph_node and also +typically inherits from oneapi::tbb::flow::sender , oneapi::tbb::flow::receiver or +both. A node performs some operation, usually on an incoming message and +may generate zero or more output messages. Some nodes require more than +one input message or generate more than one output message. + + +While it is possible to define your own node types by inheriting from +graph_node, sender and receiver, it is more typical that predefined node +types are used to construct a graph. + + +A ``function_node`` is a predefined type available in ``flow_graph.h`` and +represents a simple function with one input and one output. The +constructor for a ``function_node`` takes three arguments: + + +:: + + + template< typename Body> function_node(graph &g, size_t concurrency, Body body) + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Parameter + - Description + * - Body + - Type of the body object. + * - g + - The graph the node belongs to. + * - concurrency + - The concurrency limit for the node. You can use the concurrency limit to control how many invocations of the node are allowed to proceed concurrently, from 1 (serial) to an unlimited number. + * - body + - User defined function object, or lambda expression, that is applied to the incoming message to generate the outgoing message. + + + + +Below is code for creating a simple graph that contains a single +function_node. In this example, a node n is constructed that belongs to +graph g, and has a second argument of 1, which allows at most 1 +invocation of the node to occur concurrently. The body is a lambda +expression that prints each value v that it receives, spins for v +seconds, prints the value again, and then returns v unmodified. The code +for the function spin_for is not provided. + + +:: + + + graph g; + function_node< int, int > n( g, 1, []( int v ) -> int { + cout << v; + spin_for( v ); + cout << v; + return v; + } ); + + +After the node is constructed in the example above, you can pass +messages to it, either by connecting it to other nodes using edges or by +invoking its function try_put. Using edges is described in the next +section. + + +:: + + + n.try_put( 1 ); + n.try_put( 2 ); + n.try_put( 3 ); + + +You can then wait for the messages to be processed by calling +wait_for_all on the graph object: + + +:: + + + g.wait_for_all(); + + +In the above example code, the function_node n was created with a +concurrency limit of 1. When it receives the message sequence 1, 2 and +3, the node n will spawn a task to apply the body to the first input, 1. +When that task is complete, it will then spawn another task to apply the +body to 2. And likewise, the node will wait for that task to complete +before spawning a third task to apply the body to 3. The calls to +try_put do not block until a task is spawned; if a node cannot +immediately spawn a task to process the message, the message will be +buffered in the node. When it is legal, based on concurrency limits, a +task will be spawned to process the next buffered message. + + +In the above graph, each message is processed sequentially. If however, +you construct the node with a different concurrency limit, parallelism +can be achieved: + + +:: + + + function_node< int, int > n( g, oneapi::tbb::flow::unlimited, []( int v ) -> int { + cout << v; + spin_for( v ); + cout << v; + return v; + } ); + + +You can use unlimited as the concurrency limit to instruct the library +to spawn a task as soon as a message arrives, regardless of how many +other tasks have been spawned. You can also use any specific value, such +as 4 or 8, to limit concurrency to at most 4 or 8, respectively. It is +important to remember that spawning a task does not mean creating a +thread. So while a graph may spawn many tasks, only the number of +threads available in the library's thread pool will be used to execute +these tasks. + + +Suppose you use unlimited in the function_node constructor instead and +call try_put on the node: + + +:: + + + n.try_put( 1 ); + n.try_put( 2 ); + n.try_put( 3 ); + g.wait_for_all(); + + +The library spawns three tasks, each one applying n's lambda expression +to one of the messages. If you have a sufficient number of threads +available on your system, then all three invocations of the body will +occur in parallel. If however, you have only one thread in the system, +they execute sequentially. + diff --git a/_sources/main/tbb_userguide/Non-Linear_Pipelines.rst b/_sources/main/tbb_userguide/Non-Linear_Pipelines.rst new file mode 100644 index 0000000000..b458801655 --- /dev/null +++ b/_sources/main/tbb_userguide/Non-Linear_Pipelines.rst @@ -0,0 +1,62 @@ +.. _Non-Linear_Pipelines: + +Non-Linear Pipelines +==================== + + +Template function ``parallel_pipeline`` supports only linear pipelines. +It does not directly handle more baroque plumbing, such as in the +diagram below. + + +.. container:: fignone + :name: image011 + + + |image0| + + +However, you can still use a pipeline for this. Just topologically sort +the filters into a linear order, like this: + + +The light gray arrows are the original arrows that are now implied by +transitive closure of the other arrows. It might seem that lot of +parallelism is lost by forcing a linear order on the filters, but in +fact the only loss is in the *latency* of the pipeline, not the +throughput. The latency is the time it takes a token to flow from the +beginning to the end of the pipeline. Given a sufficient number of +processors, the latency of the original non-linear pipeline is three +filters. This is because filters A and B could process the token +concurrently, and likewise filters D and E could process the token +concurrently. + + +.. container:: fignone + :name: image012 + + + |image1| + + +In the linear pipeline, the latency is five filters. The behavior of +filters A, B, D and E above may need to be modified in order to properly +handle objects that don’t need to be acted upon by the filter other than +to be passed along to the next filter in the pipeline. + + +The throughput remains the same, because regardless of the topology, the +throughput is still limited by the throughput of the slowest serial +filter. If ``parallel_pipeline`` supported non-linear pipelines, it +would add a lot of programming complexity, and not improve throughput. +The linear limitation of ``parallel_pipeline`` is a good tradeoff of +gain versus pain. + + +.. |image0| image:: Images/image011.jpg + :width: 281px + :height: 107px +.. |image1| image:: Images/image012.jpg + :width: 281px + :height: 107px + diff --git a/_sources/main/tbb_userguide/Package_Contents_os.rst b/_sources/main/tbb_userguide/Package_Contents_os.rst new file mode 100644 index 0000000000..2d9e0a2fe4 --- /dev/null +++ b/_sources/main/tbb_userguide/Package_Contents_os.rst @@ -0,0 +1,18 @@ +.. _Package_Contents: + +Package Contents +================ + + +|full_name| includes dynamic library files and header files for Windows\*, Linux\* +and macOS\* operating systems as described in this section. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Debug_Versus_Release_Libraries + ../tbb_userguide/Scalable_Memory_Allocator + ../tbb_userguide/Windows_OS_ug + ../tbb_userguide/Linux_OS + ../tbb_userguide/Mac_OS + diff --git a/_sources/main/tbb_userguide/Parallelizing_Complex_Loops.rst b/_sources/main/tbb_userguide/Parallelizing_Complex_Loops.rst new file mode 100644 index 0000000000..88b1cdba44 --- /dev/null +++ b/_sources/main/tbb_userguide/Parallelizing_Complex_Loops.rst @@ -0,0 +1,17 @@ +.. _Parallelizing_Complex_Loops: + +Parallelizing Complex Loops +=========================== + + +You can successfully parallelize many applications using only the +constructs in the **Parallelizing Simple Loops** section. However, some +situations call for other parallel patterns. This section describes the +support for some of these alternate patterns. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Cook_Until_Done_parallel_do + ../tbb_userguide/Working_on_the_Assembly_Line_pipeline + ../tbb_userguide/Summary_of_Loops_and_Pipelines \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Parallelizing_Flow_Graph.rst b/_sources/main/tbb_userguide/Parallelizing_Flow_Graph.rst new file mode 100644 index 0000000000..5d3fc7bf1e --- /dev/null +++ b/_sources/main/tbb_userguide/Parallelizing_Flow_Graph.rst @@ -0,0 +1,102 @@ +.. _Parallelizing_Flow_Graph: + +Parallelizing Data Flow and Dependency Graphs +============================================= + + +In addition to loop parallelism, the |full_name| library also supports graph parallelism. +It's possible to create graphs that are highly scalable, but it is also possible to +create graphs that are completely sequential. + + +Using graph parallelism, computations are represented by nodes and the +communication channels between these computations are represented by +edges. When a node in the graph receives a message, a task is spawned to +execute its body object on the incoming message. Messages flow through +the graph across the edges that connect the nodes. The following +sections present two examples of applications that can be expressed as +graphs. + + +The following figure shows a *streaming* or *data flow* application +where a sequence of values is processed as each value passes through the +nodes in the graph. In this example, the sequence is created by a +function F. For each value in the sequence, G squares the value and H +cubes the value. J then takes each of the squared and cubed values and +adds them to a global sum. After all values in the sequence are +completely processed, sum is equal to the sum of the sequence of squares +and cubes from 1 to 10. In a streaming or data flow graph, the values +actually flow across the edges; the output of one node becomes the input +of its successor(s). + + +.. container:: fignone + :name: simple_data_flow_title + + + **Simple Data Flow Graph** + + + .. container:: imagecenter + + + |image0| + + +The following graphic shows a different form of graph application. In +this example, a dependence graph is used to establish a partial ordering +among the steps for making a peanut butter and jelly sandwich. In this +partial ordering, you must first get the bread before spreading the +peanut butter or jelly on the bread. You must spread on the peanut +butter before you put away the peanut butter jar, and likewise spread on +the jelly before you put away the jelly jar. And, you need to spread on +both the peanut butter and jelly before putting the two slices of bread +together. This is a partial ordering because, for example, it doesn't +matter if you spread on the peanut butter first or the jelly first. It +also doesn't matter if you finish making the sandwich before putting +away the jars. + + +.. container:: fignone + :name: dependence_graph_make_sandwitch + + + **Dependence Graph for Making a Sandwich** + + + .. container:: imagecenter + + + |image1| + + +While it can be inferred that resources, such as the bread, or the jelly +jar, are shared between ordered steps, it is not explicit in the graph. +Instead, only the required ordering of steps is explicit in a dependence +graph. For example, you must "Put jelly on 1 slice" **before** you "Put +away jelly jar". + + +The flow graph interface in the oneTBB library allows you to express +data flow and dependence graphs such as these, as well as more +complicated graphs that include cycles, conditionals, buffering and +more. If you express your application using the flow graph interface, +the runtime library spawns tasks to exploit the parallelism that is +present in the graph. For example, in the first example above, perhaps +two different values might be squared in parallel, or the same value +might be squared and cubed in parallel. Likewise in the second example, +the peanut butter might be spread on one slice of bread in parallel with +the jelly being spread on the other slice. The interface expresses what +is legal to execute in parallel, but allows the runtime library to +choose at runtime what will be executed in parallel. + + +The support for graph parallelism is contained within the namespace +``oneapi::tbb::flow`` and is defined in the ``flow_graph.h`` header file. + + +.. |image0| image:: Images/flow_graph.jpg +.. |image1| image:: Images/flow_graph_complex.jpg + :width: 440px + :height: 337px + diff --git a/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_os.rst b/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_os.rst new file mode 100644 index 0000000000..fce366598f --- /dev/null +++ b/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_os.rst @@ -0,0 +1,40 @@ +.. _Parallelizing_Simple_Loops: + +Parallelizing Simple Loops +========================== + + +The simplest form of scalable parallelism is a loop of iterations that +can each run simultaneously without interfering with each other. The +following sections demonstrate how to parallelize simple loops. + + +.. note:: + |full_name| components are + defined in namespace ``tbb``. For brevity’s sake, the namespace is + explicit in the first mention of a component, but implicit + afterwards. + + +When compiling oneTBB programs, be sure to link in the oneTBB shared +library, otherwise undefined references will occur. The following table +shows compilation commands that use the debug version of the library. +Remove the "``_debug``" portion to link against the production version +of the library. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Operating System + - Command line + * - Windows\* OS + - ``icl /MD example.cpp tbb_debug.lib`` + * - Linux\* OS + - ``icc example.cpp -ltbb_debug`` + + +.. include:: Parallelizing_Simple_Loops_toctree.rst \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_toctree.rst b/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_toctree.rst new file mode 100644 index 0000000000..f83bb1b244 --- /dev/null +++ b/_sources/main/tbb_userguide/Parallelizing_Simple_Loops_toctree.rst @@ -0,0 +1,11 @@ +.. _Parallelizing_Simple_Loops_toctree: + + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Initializing_and_Terminating_the_Library + ../tbb_userguide/parallel_for_os + ../tbb_userguide/parallel_reduce + ../tbb_userguide/Advanced_Example + ../tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Partitioner_Summary.rst b/_sources/main/tbb_userguide/Partitioner_Summary.rst new file mode 100644 index 0000000000..7cc5171296 --- /dev/null +++ b/_sources/main/tbb_userguide/Partitioner_Summary.rst @@ -0,0 +1,64 @@ +.. _Partitioner_Summary: + +Partitioner Summary +=================== + + +The parallel loop templates ``parallel_for`` and ``parallel_reduce`` +take an optional *partitioner* argument, which specifies a strategy for +executing the loop. The following table summarizes partitioners and +their effect when used in conjunction with ``blocked_range``. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Partitioner + - Description + - When Used with ``blocked_range(i,j,g)`` + * - ``simple_partitioner`` + - Chunksize bounded by grain size. + - ``g/2 ≤ chunksize ≤ g`` + * - ``auto_partitioner`` (default) + - Automatic chunk size. + - ``g/2 ≤ chunksize`` + * - ``affinity_partitioner`` + - Automatic chunk size, cache affinity and uniform distribution of iterations. + - ``g/2 ≤ chunksize`` + * - ``static_partitioner`` + - Deterministic chunk size, cache affinity and uniform distribution of iterations without load balancing. + - ``max(g/3, problem_size/num_of_resources) ≤ chunksize`` + + + + +An ``auto_partitioner`` is used when no partitioner is specified. In +general, the ``auto_partitioner`` or ``affinity_partitioner`` should be +used, because these tailor the number of chunks based on available +execution resources. ``affinity_partitioner`` and ``static_partitioner`` +may take advantage of ``Range`` ability to split in a given ratio (see +"Advanced Topic: Other Kinds of Iteration Spaces") for distributing +iterations in nearly equal chunks between computing resources. + + +``simple_partitioner`` can be useful in the following situations: + + +- The subrange size for ``operator()`` must not exceed a limit. That + might be advantageous, for example, if your ``operator()`` needs a + temporary array proportional to the size of the range. With a limited + subrange size, you can use an automatic variable for the array + instead of having to use dynamic memory allocation. + + +- A large subrange might use cache inefficiently. For example, suppose + the processing of a subrange involves repeated sweeps over the same + memory locations. Keeping the subrange below a limit might enable the + repeatedly referenced memory locations to fit in cache. + + +- You want to tune to a specific machine. + diff --git a/_sources/main/tbb_userguide/Predefined_Node_Types.rst b/_sources/main/tbb_userguide/Predefined_Node_Types.rst new file mode 100644 index 0000000000..14c009f0d8 --- /dev/null +++ b/_sources/main/tbb_userguide/Predefined_Node_Types.rst @@ -0,0 +1,54 @@ +.. _Predefined_Node_Types: + +Predefined Node Types +===================== + + +You can define your own node types by inheriting from class graph_node, +class sender and class receiver but it is likely that you can create +your graph with the predefined node types already available in +flow_graph.h. Below is a table that lists all of the predefined types +with a basic description. See the Developer Reference for a more +detailed description of each node. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + :widths: 25 25 + + * - Predefined Node Type + - Description + * - input_node + - A single-output node, with a generic output type. + When activated, it executes a user body to generate its output. Its body is invoked if downstream nodes have accepted the previous generated output. + Otherwise, the previous output is temporarily buffered until it is accepted downstream and then the body is again invoked. + * - function_node + - A single-input single-output node that broadcasts its output to all successors. Has generic input and output types. Executes a user body, and has controllable concurrency level and buffering policy. For each input exactly one output is returned. + * - continue_node + - A single-input, single-output node that broadcasts its output to all successors. It has a single input that requires 1 or more inputs of type continue_msg and has a generic output type. It executes a user body when it receives N continue_msg objects at its input. N is equal to the number of predecessors plus any additional offset assigned at construction time. + * - multifunction_node + - A single-input multi-output node. It has a generic input type and several generic output types. It executes a user body, and has controllable concurrency level and buffering policy. The body can output zero or more messages on each output port. + * - broadcast_node + - A single-input, single-output node that broadcasts each message received to all successors. Its input and output are of the same generic type. It does not buffer messages. + * - buffer_node, queue_node, priority_queue_node, and sequencer_node. + - Single-input, single-output nodes that buffer messages and send their output to one successor. The order in which the messages are sent are node specific (see the Developer Reference). These nodes are unique in that they send to only a single successor and not all successors. + * - join_node + - A multi-input, single-output node. There are several generic input types and the output type is a tuple of these generic types. The node combines one message from each input port to create a tuple that is broadcast to all successors. The policy used to combine messages is selectable as queueing, reserving or tag-matching. + * - split_node + - A single-input, multi-output node. The input type is a tuple of generic types and there is one output port for each of the types in the tuple. The node receives a tuple of values and outputs each element of the tuple on a corresponding output port. + * - write_once_node, overwrite_node + - Single-input, single-output nodes that buffer a single message and broadcast their outputs to all successors. After broadcast, the nodes retain the last message received, so it is available to any future successor. A write_once_node will only accept the first message it receives, while the overwrite_node will accept all messages, broadcasting them to all successors, and replacing the old value with the new. + * - limiter_node + - A multi-input, single output node that broadcasts its output to all successors. The main input type and output type are of the same generic type. The node increments an internal counter when it broadcasts a message. If the increment causes it to reach its user-assigned threshold, it will broadcast no more messages. A special input port can be used to adjust the internal count, allowing further messages to be broadcast. The node does not buffer messages. + * - indexer_node + - A multi-input, single-output node that broadcasts its output message to all of its successors. The input type is a list of generic types and the output type is a tagged_msg. The message is of one of the types listed in the input and the tag identifies the port on which the message was received. Messages are broadcast individually as they arrive at the input ports. + * - composite_node + - A node that might have 0, 1 or multiple ports for both input and output. The composite_node packages a group of other nodes together and maintains a tuple of references to ports that border it. This allows for the corresponding ports of the composite_node to be used to make edges which hitherto would have been made from the actual nodes in the composite_node. + * - async_node (preview feature) + - A node that allows a flow graph to communicate with an external activity managed by the user or another runtime. This node receives messages of generic type, invokes the user-provided body to submit a message to an external activity. The external activity can use a special interface to return a generic type and put it to all successors of async_node. + + + diff --git a/_sources/main/tbb_userguide/Reader_Writer_Mutexes.rst b/_sources/main/tbb_userguide/Reader_Writer_Mutexes.rst new file mode 100644 index 0000000000..0303d0c31a --- /dev/null +++ b/_sources/main/tbb_userguide/Reader_Writer_Mutexes.rst @@ -0,0 +1,21 @@ +.. _Reader_Writer_Mutexes: + +Reader Writer Mutexes +===================== + + +Mutual exclusion is necessary when at least one thread *writes* to a +shared variable. But it does no harm to permit multiple readers into a +protected region. The reader-writer variants of the mutexes, denoted by +``_rw_`` in the class names, enable multiple readers by distinguishing +*reader locks* from *writer locks.* There can be more than one reader +lock on a given mutex. + + +Requests for a reader lock are distinguished from requests for a writer +lock via an extra boolean parameter in the constructor for +``scoped_lock``. The parameter is false to request a reader lock and +true to request a writer lock. It defaults to ``true`` so that when +omitted, a ``spin_rw_mutex`` or ``queuing_rw_mutex`` behaves like its +non-``_rw_`` counterpart. + diff --git a/_sources/main/tbb_userguide/References.rst b/_sources/main/tbb_userguide/References.rst new file mode 100644 index 0000000000..d6ab0d3901 --- /dev/null +++ b/_sources/main/tbb_userguide/References.rst @@ -0,0 +1,19 @@ +.. _References: + +References +========== + + +**[1]**   "Memory Consistency & .NET", Arch D. Robison, Dr. Dobb’s +Journal, April 2003. + + +**[2]**   A Formal Specification of Intel® Itanium® Processor Family +Memory Ordering, Intel Corporation, October 2002. + + +**[3]**   "Cilk: An Efficient Multithreaded Runtime System", Robert +Blumofe, Christopher Joerg, Bradley Kuszmaul, C. Leiserson, and Keith +Randall, Proceedings of the fifth ACM SIGPLAN symposium on Principles +and practice of parallel programming, 1995. + diff --git a/_sources/main/tbb_userguide/Scalable_Memory_Allocator.rst b/_sources/main/tbb_userguide/Scalable_Memory_Allocator.rst new file mode 100644 index 0000000000..922033b76e --- /dev/null +++ b/_sources/main/tbb_userguide/Scalable_Memory_Allocator.rst @@ -0,0 +1,15 @@ +.. _Scalable_Memory_Allocator: + +Scalable Memory Allocator +========================= + + +Both the debug and release versions of |full_name| +consists of two dynamic shared libraries, one with +general support and the other with a scalable memory allocator. The +latter is distinguished by ``malloc`` in its name. For example, the +release versions for Windows\* OS are ``tbb.dll`` and ``tbbmalloc.dll`` +respectively. Applications may choose to use only the general library, +or only the scalable memory allocator, or both. See the links below for +more information on memory allocation. + diff --git a/_sources/main/tbb_userguide/Summary_of_Containers.rst b/_sources/main/tbb_userguide/Summary_of_Containers.rst new file mode 100644 index 0000000000..53b7049c96 --- /dev/null +++ b/_sources/main/tbb_userguide/Summary_of_Containers.rst @@ -0,0 +1,11 @@ +.. _Summary_of_Containers: + +Summary of Containers +===================== + + +The high-level containers in |full_name| +enable common idioms for concurrent access. They are suitable for +scenarios where the alternative would be a serial container with a lock +around it. + diff --git a/_sources/main/tbb_userguide/Summary_of_Loops_and_Pipelines.rst b/_sources/main/tbb_userguide/Summary_of_Loops_and_Pipelines.rst new file mode 100644 index 0000000000..a549d293dc --- /dev/null +++ b/_sources/main/tbb_userguide/Summary_of_Loops_and_Pipelines.rst @@ -0,0 +1,10 @@ +.. _Summary_of_Loops_and_Pipelines: + +Summary of Loops and Pipelines +============================== + +The high-level loop and pipeline templates in |full_name| +give you efficient scalable ways to exploit the power of multi-core chips without having to start from scratch. +They let you design your software at a high task-pattern level and not worry about low-level manipulation of threads. +Because they are generic, you can customize them to your specific needs. +Have fun using these templates to unlock the power of multi-core. diff --git a/_sources/main/tbb_userguide/Task-Based_Programming.rst b/_sources/main/tbb_userguide/Task-Based_Programming.rst new file mode 100644 index 0000000000..94e53545ee --- /dev/null +++ b/_sources/main/tbb_userguide/Task-Based_Programming.rst @@ -0,0 +1,92 @@ +.. _Task-Based_Programming: + +Task-Based Programming +====================== + + +When striving for performance, programming in terms of threads can be a +poor way to do multithreaded programming. It is much better to formulate +your program in terms of *logical tasks*, not threads, for several +reasons. + + +- Matching parallelism to available resources + + +- Faster task startup and shutdown + + +- More efficient evaluation order + + +- Improved load balancing + + +- Higher–level thinking + + +The following paragraphs explain these points in detail. + + +The threads you create with a threading package are *logical* threads, +which map onto the *physical threads* of the hardware. For computations +that do not wait on external devices, highest efficiency usually occurs +when there is exactly one running logical thread per physical thread. +Otherwise, there can be inefficiencies from the mismatch\ *. +Undersubscription* occurs when there are not enough running logical +threads to keep the physical threads working. *Oversubscription* occurs +when there are more running logical threads than physical threads. +Oversubscription usually leads to *time sliced* execution of logical +threads, which incurs overheads as discussed in Appendix A, *Costs of +Time Slicing*. The scheduler tries to avoid oversubscription, by having +one logical thread per physical thread, and mapping tasks to logical +threads, in a way that tolerates interference by other threads from the +same or other processes. + + +The key advantage of tasks versus logical threads is that tasks are much +*lighter weight* than logical threads. On Linux systems, starting and +terminating a task is about 18 times faster than starting and +terminating a thread. On Windows systems, the ratio is more than 100. +This is because a thread has its own copy of a lot of resources, such as +register state and a stack. On Linux, a thread even has its own process +id. A task in |full_name|, in contrast, is +typically a small routine, and also, cannot be preempted at the task +level (though its logical thread can be preempted). + + +Tasks in oneTBB are efficient too because *the scheduler is unfair*. Thread schedulers typically +distribute time slices in a round-robin fashion. This distribution is +called "fair", because each logical thread gets its fair share of time. +Thread schedulers are typically fair because it is the safest strategy +to undertake without understanding the higher-level organization of a +program. In task-based programming, the task scheduler does have some +higher-level information, and so can sacrifice fairness for efficiency. +Indeed, it often delays starting a task until it can make useful +progress. + + +The scheduler does *load balancing*. In addition to using the right +number of threads, it is important to distribute work evenly across +those threads. As long as you break your program into enough small +tasks, the scheduler usually does a good job of assigning tasks to +threads to balance load. With thread-based programming, you are often +stuck dealing with load-balancing yourself, which can be tricky to get +right. + + +.. tip:: + Design your programs to try to create many more tasks than there are + threads, and let the task scheduler choose the mapping from tasks to + threads. + + +Finally, the main advantage of using tasks instead of threads is that +they let you think at a higher, task-based, level. With thread-based +programming, you are forced to think at the low level of physical +threads to get good efficiency, because you have one logical thread per +physical thread to avoid undersubscription or oversubscription. You also +have to deal with the relatively coarse grain of threads. With tasks, +you can concentrate on the logical dependences between tasks, and leave +the efficient scheduling to the scheduler. + diff --git a/_sources/main/tbb_userguide/Task_Scheduler_Bypass.rst b/_sources/main/tbb_userguide/Task_Scheduler_Bypass.rst new file mode 100644 index 0000000000..c198f6ac6b --- /dev/null +++ b/_sources/main/tbb_userguide/Task_Scheduler_Bypass.rst @@ -0,0 +1,20 @@ +.. _Task_Scheduler_Bypass: + +Task Scheduler Bypass +===================== + +Scheduler bypass is an optimization where you directly specify the next task to run. +According to the rules of execution described in :doc:`How Task Scheduler Works `, +the spawning of the new task to be executed by the current thread involves the next steps: + + - Push a new task onto the thread's deque. + - Continue to execute the current task until it is completed. + - Take a task from the thread's deque, unless it is stolen by another thread. + +Steps 1 and 3 introduce unnecessary deque operations or, even worse, allow stealing that can hurt +locality without adding significant parallelism. These problems can be avoided by using "Task Scheduler Bypass" technique to directly point the preferable task to be executed next +instead of spawning it. When, as described in :doc:`How Task Scheduler Works `, +the returned task becomes the first candidate for the next task to be executed by the thread. Furthermore, this approach almost guarantees that +the task is executed by the current thread and not by any other thread. + +Please note that at the moment the only way to use this optimization is to use `preview feature of ``onepai::tbb::task_group`` \ No newline at end of file diff --git a/_sources/main/tbb_userguide/The_Task_Scheduler.rst b/_sources/main/tbb_userguide/The_Task_Scheduler.rst new file mode 100644 index 0000000000..d9e6056e1c --- /dev/null +++ b/_sources/main/tbb_userguide/The_Task_Scheduler.rst @@ -0,0 +1,21 @@ +.. _The_Task_Scheduler: + +The Task Scheduler +================== + + +This section introduces the |full_name| +*task scheduler*. The task scheduler is the engine that powers the loop +templates. When practical, use the loop templates instead of +the task scheduler, because the templates hide the complexity of the +scheduler. However, if you have an algorithm that does not naturally map +onto one of the high-level templates, use the task scheduler. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Task-Based_Programming + ../tbb_userguide/When_Task-Based_Programming_Is_Inappropriate + ../tbb_userguide/How_Task_Scheduler_Works + ../tbb_userguide/Task_Scheduler_Bypass + ../tbb_userguide/Guiding_Task_Scheduler_Execution \ No newline at end of file diff --git a/_sources/main/tbb_userguide/Throughput_of_pipeline.rst b/_sources/main/tbb_userguide/Throughput_of_pipeline.rst new file mode 100644 index 0000000000..9d1c906a17 --- /dev/null +++ b/_sources/main/tbb_userguide/Throughput_of_pipeline.rst @@ -0,0 +1,35 @@ +.. _Throughput_of_pipeline: + +Throughput of pipeline +====================== + + +The throughput of a pipeline is the rate at which tokens flow through +it, and is limited by two constraints. First, if a pipeline is run with +``N`` tokens, then obviously there cannot be more than ``N`` operations +running in parallel. Selecting the right value of ``N`` may involve some +experimentation. Too low a value limits parallelism; too high a value +may demand too many resources (for example, more buffers). Second, the +throughput of a pipeline is limited by the throughput of the slowest +sequential filter. This is true even for a pipeline with no parallel +filters. No matter how fast the other filters are, the slowest +sequential filter is the bottleneck. So in general you should try to +keep the sequential filters fast, and when possible, shift work to the +parallel filters. + + +The text processing example has relatively poor speedup, because the +serial filters are limited by the I/O speed of the system. Indeed, even +with files that are on a local disk, you are unlikely to see a speedup +much more than 2. To really benefit from a pipeline, the parallel +filters need to be doing some heavy lifting compared to the serial +filters. + + +The window size, or sub-problem size for each token, can also limit +throughput. Making windows too small may cause overheads to dominate the +useful work. Making windows too large may cause them to spill out of +cache. A good guideline is to try for a large window size that still +fits in cache. You may have to experiment a bit to find a good window +size. + diff --git a/_sources/main/tbb_userguide/Timing.rst b/_sources/main/tbb_userguide/Timing.rst new file mode 100644 index 0000000000..89c2f695f4 --- /dev/null +++ b/_sources/main/tbb_userguide/Timing.rst @@ -0,0 +1,52 @@ +.. _Timing: + +Timing +====== + + +When measuring the performance of parallel programs, it is usually *wall +clock* time, not CPU time, that matters. The reason is that better +parallelization typically increases aggregate CPU time by employing more +CPUs. The goal of parallelizing a program is usually to make it run +*faster* in real time. + + +The class ``tick_count`` in |full_name| +provides a simple interface for measuring wall clock time. A +``tick_count`` value obtained from the static method tick_count::now() +represents the current absolute time. Subtracting two ``tick_count`` +values yields a relative time in ``tick_count::interval_t``, which you +can convert to seconds, as in the following example: + + +:: + + + tick_count t0 = tick_count::now(); + ... do some work ... + tick_count t1 = tick_count::now(); + printf("work took %g seconds\n",(t1-t0).seconds()); + + + + +Unlike some timing interfaces, ``tick_count`` is guaranteed to be safe +to use across threads. It is valid to subtract ``tick_count`` values +that were created by different threads. A ``tick_count`` difference can +be converted to seconds. + + +The resolution of ``tick_count`` corresponds to the highest resolution +timing service on the platform that is valid across threads in the same +process. Since the CPU timer registers are *not* valid across threads on +some platforms, this means that the resolution of tick_count can not be +guaranteed to be consistent across platforms. + + +.. note:: + + On Linux\* OS, you may need to add -lrt to the linker command when + you use oneapi::tbb::tick_count class. For more information, see + `http://fedoraproject.org/wiki/Features/ChangeInImplicitDSOLinking + `_. + diff --git a/_sources/main/tbb_userguide/UpgradeDowngrade.rst b/_sources/main/tbb_userguide/UpgradeDowngrade.rst new file mode 100644 index 0000000000..11324b92e3 --- /dev/null +++ b/_sources/main/tbb_userguide/UpgradeDowngrade.rst @@ -0,0 +1,51 @@ +.. _UpgradeDowngrade: + +Upgrade/Downgrade +================= + + +It is possible to upgrade a reader lock to a writer lock, by using the +method ``upgrade_to_writer``. Here is an example. + + +:: + + + std::vector MyVector; + typedef spin_rw_mutex MyVectorMutexType; + MyVectorMutexType MyVectorMutex; +   + + void AddKeyIfMissing( const string& key ) { + // Obtain a reader lock on MyVectorMutex + MyVectorMutexType::scoped_lock lock(MyVectorMutex,/*is_writer=*/false); + size_t n = MyVector.size(); + for( size_t i=0; i`` requires the |full_name| +scalable memory allocator library as +described in **Scalable Memory Allocator**. It does not require the +oneTBB general library, and can be used independently of the rest of +oneTBB. + + +The templates ``tbb_allocator`` and ``cache_aligned_allocator`` +use the scalable allocator library if it is present otherwise it reverts +to using ``malloc`` and ``free``. Thus, you can use these templates even +in applications that choose to omit the scalable memory allocator +library. + + +The rest of |full_name| can be used +with or without the oneTBB scalable memory allocator library. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Template + - Requirements + - Notes + * - \ ``scalable_allocator`` + - |full_name| scalable memory allocator library. See **Scalable Memory Allocator**. + -   + * - \ ``tbb_allocator`` \ ``cache_aligned_allocator`` + -   + - Uses the scalable allocator library if it is present, otherwise it reverts to using ``malloc`` and ``free``. + + + diff --git a/_sources/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst b/_sources/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst new file mode 100644 index 0000000000..cd2d2e1a93 --- /dev/null +++ b/_sources/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst @@ -0,0 +1,83 @@ +.. _Windows_C_Dynamic_Memory_Interface_Replacement: + +Windows\* OS C/C++ Dynamic Memory Interface Replacement +======================================================= + + +Release version of the proxy library is ``tbbmalloc_proxy.dll``, debug +version is ``tbbmalloc_proxy_debug.dll``. + + +The following dynamic memory functions are replaced: + + +- Standard C library functions: ``malloc``, ``calloc``, ``realloc``, + ``free`` + + +- Replaceable global C++ operators ``new`` and ``delete`` + + +- Microsoft\* C run-time library functions: ``_msize``, + ``_aligned_malloc``, ``_aligned_realloc``, ``_aligned_free``, + ``_aligned_msize`` + + +.. note:: + Replacement of memory allocation functions is not supported for + Universal Windows Platform applications. + + +To do the replacement use one of the following methods: + + +- Add the following header to a source code of any binary which is + loaded during application startup. + + + :: + + + #include "oneapi/tbb/tbbmalloc_proxy.h" + + +- Alternatively, add the following parameters to the linker options for + the .exe or .dll file that is loaded during application startup. + + For 32-bit code (note the triple underscore): + + + :: + + + tbbmalloc_proxy.lib /INCLUDE:"___TBB_malloc_proxy" + + For 64-bit code (note the double underscore): + + + :: + + + tbbmalloc_proxy.lib /INCLUDE:"__TBB_malloc_proxy" + + +The OS program loader must be able to find the proxy library and the +scalable memory allocator library at program load time. For that you may +include the directory containing the libraries in the ``PATH`` +environment variable. + + +The replacement uses in-memory binary instrumentation of Visual C++\* +runtime libraries. To ensure correctness, it must first recognize a +subset of dynamic memory functions in these libraries. If a problem +occurs, the replacement is skipped, and the program continues to use the +standard memory allocation functions. You can use the ``TBB_malloc_replacement_log`` +function to check if the replacement has succeeded and to get additional information. + + +Set the ``TBB_MALLOC_DISABLE_REPLACEMENT`` environment variable to 1 to +disable replacement for a specific program invocation. In this case, the +program will use standard dynamic memory allocation functions. Note that +the oneTBB memory allocation libraries are still required for the +program to start even if their usage is disabled. + diff --git a/_sources/main/tbb_userguide/Windows_OS_ug.rst b/_sources/main/tbb_userguide/Windows_OS_ug.rst new file mode 100644 index 0000000000..85fc3306ce --- /dev/null +++ b/_sources/main/tbb_userguide/Windows_OS_ug.rst @@ -0,0 +1,66 @@ +.. _Windows_OS_ug: + +Windows\* +========= + +This section uses <*tbb_install_dir*> to indicate the top-level +installation directory. The following table describes the subdirectory +structure for Windows\*, relative to <*tbb_install_dir*>. + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Item + - Location + - Environment Variable + * - Header files + - | ``include\oneapi\tbb.h`` + | ``include\oneapi\tbb\*.h`` + - ``INCLUDE`` + * - .lib files + - ``lib\\vc\.lib``\ + - ``LIB`` + * - .dll files + - ``redist\\vc\.dll`` + - ``PATH`` + * - .pdb files + - Same as corresponding ``.dll`` file. + - \ + +Where + +* ```` - ``ia32`` or ``intel64`` + + .. note:: Starting with oneTBB 2022.0, 32-bit binaries are supported only by the open-source version of the library. + +* ```` - ``tbb``, ``tbbmalloc``, ``tbbmalloc_proxy`` or ``tbbbind`` +* ```` + + - ``14`` - use for dynamic linkage with the CRT + + - ``14_uwp`` - use for Windows 10 Universal Windows applications + + - ``14_uwd`` - use for Universal Windows Drivers + + - ``_mt`` - use for static linkage with the CRT + +* ```` - ``_debug`` or empty +* ```` - binary version + +The last column shows, which environment variables are used by the +Microsoft\* Visual C++\* or Intel® C++ Compiler Classic or Intel® oneAPI DPC++/C++ Compiler, to find these +subdirectories. + +.. CAUTION:: + Ensure that the relevant product directories are mentioned by the + environment variables; otherwise the compiler might not find the + required files. + + +.. note:: + Microsoft\* C/C++ run-time libraries come in static and dynamic + forms. Either can be used with oneTBB. Linking to the oneTBB library + is always dynamic. diff --git a/_sources/main/tbb_userguide/Working_on_the_Assembly_Line_pipeline.rst b/_sources/main/tbb_userguide/Working_on_the_Assembly_Line_pipeline.rst new file mode 100644 index 0000000000..15299b5151 --- /dev/null +++ b/_sources/main/tbb_userguide/Working_on_the_Assembly_Line_pipeline.rst @@ -0,0 +1,356 @@ +.. _Working_on_the_Assembly_Line_pipeline: + +Working on the Assembly Line: parallel_pipeline +=============================================== + + +*Pipelining* is a common parallel pattern that mimics a traditional +manufacturing assembly line. Data flows through a series of pipeline +filters and each filter processes the data in some way. Given an +incoming stream of data, some of these filters can operate in parallel, +and others cannot. For example, in video processing, some operations on +frames do not depend on other frames, and so can be done on multiple +frames at the same time. On the other hand, some operations on frames +require processing prior frames first. + + +The |full_name| classes +``parallel_pipeline`` and filter implement the pipeline pattern. A +simple text processing example will be used to demonstrate the usage of +``parallel_pipeline`` and filter to perform parallel formatting. The +example reads a text file, squares each decimal numeral in the text, and +writes the modified text to a new file. Below is a picture of the +pipeline. + + +.. CAUTION:: + Since the body object provided to the filters of the + ``parallel_pipeline`` might be copied, its ``operator()`` should not + modify the body. Otherwise the modification might or might not become + visible to the thread that invoked ``parallel_pipeline``, depending + upon whether ``operator()`` is acting on the original or a copy. As a + reminder of this nuance, ``parallel_pipeline`` requires that the body + object's ``operator()`` be declared ``const``. + + +.. container:: tablenoborder + + + .. list-table:: + :header-rows: 0 + + * - Read chunk from input file + - |image0| + - Square numerals in chunk + - |image1| + - Write chunk to output file + + + + +Assume that the raw file I/O is sequential. The squaring filter can be +done in parallel. That is, if you can serially read ``n`` chunks very +quickly, you can transform each of the ``n`` chunks in parallel, as long +as they are written in the proper order to the output file. Though the +raw I/O is sequential, the formatting of input and output can be moved +to the middle filter, and thus be parallel. + + +To amortize parallel scheduling overheads, the filters operate on chunks +of text. Each input chunk is approximately 4000 characters. Each chunk +is represented by an instance of class ``TextSlice``: + + +:: + + + // Holds a slice of text. + /** Instances *must* be allocated/freed using methods herein, because the C++ declaration + represents only the header of a much larger object in memory. */ + class TextSlice { + // Pointer to one past last character in sequence + char* logical_end; + // Pointer to one past last available byte in sequence. + char* physical_end; + public: + // Allocate a TextSlice object that can hold up to max_size characters. + static TextSlice* allocate( size_t max_size ) { + // +1 leaves room for a terminating null character. + TextSlice* t = (TextSlice*)oneapi::tbb::tbb_allocator().allocate( sizeof(TextSlice)+max_size+1 ); + t->logical_end = t->begin(); + t->physical_end = t->begin()+max_size; + return t; + } + // Free this TextSlice object + void free() { + oneapi::tbb::tbb_allocator().deallocate((char*)this, sizeof(TextSlice)+(physical_end-begin())+1); + } + // Pointer to beginning of sequence + char* begin() {return (char*)(this+1);} + // Pointer to one past last character in sequence + char* end() {return logical_end;} + // Length of sequence + size_t size() const {return logical_end-(char*)(this+1);} + // Maximum number of characters that can be appended to sequence + size_t avail() const {return physical_end-logical_end;} + // Append sequence [first,last) to this sequence. + void append( char* first, char* last ) { + memcpy( logical_end, first, last-first ); + logical_end += last-first; + } + // Set end() to given value. + void set_end( char* p ) {logical_end=p;} + }; + + +Below is the top-level code for building and running the pipeline. +``TextSlice`` objects are passed between filters using pointers to avoid +the overhead of copying a ``TextSlice``. + + +:: + + + void RunPipeline( int ntoken, FILE* input_file, FILE* output_file ) { + oneapi::tbb::parallel_pipeline( + ntoken, + oneapi::tbb::make_filter( + oneapi::tbb::filter_mode::serial_in_order, MyInputFunc(input_file) ) + & + oneapi::tbb::make_filter( + oneapi::tbb::filter_mode::parallel, MyTransformFunc() ) + & + oneapi::tbb::make_filter( + oneapi::tbb::filter_mode::serial_in_order, MyOutputFunc(output_file) ) ); + } + + +The parameter ``ntoken`` to method ``parallel_pipeline`` controls the +level of parallelism. Conceptually, tokens flow through the pipeline. In +a serial in-order filter, each token must be processed serially in +order. In a parallel filter, multiple tokens can by processed in +parallel by the filter. If the number of tokens were unlimited, there +might be a problem where the unordered filter in the middle keeps +gaining tokens because the output filter cannot keep up. This situation +typically leads to undesirable resource consumption by the middle +filter. The parameter to method ``parallel_pipeline`` specifies the +maximum number of tokens that can be in flight. Once this limit is +reached, the pipeline never creates a new token at the input filter +until another token is destroyed at the output filter. + + +The second parameter specifies the sequence of filters. Each filter is +constructed by function ``make_filter(mode,functor)``. + + +- The *inputType* specifies the type of values input by a filter. For + the input filter, the type is ``void``. + + +- The *outputType* specifies the type of values output by a filter. For + the output filter, the type is ``void``. + + +- The *mode* specifies whether the filter processes items in parallel, + serial in-order, or serial out-of-order. + + +- The *functor* specifies how to produce an output value from an input + value. + + +The filters are concatenated with ``operator&``. When concatenating two +filters, the *outputType* of the first filter must match the *inputType* +of the second filter. + + +The filters can be constructed and concatenated ahead of time. An +equivalent version of the previous example that does this follows: + + +:: + + + void RunPipeline( int ntoken, FILE* input_file, FILE* output_file ) { + oneapi::tbb::filter f1( oneapi::tbb::filter_mode::serial_in_order, + MyInputFunc(input_file) ); + oneapi::tbb::filter f2(oneapi::tbb::filter_mode::parallel, + MyTransformFunc() ); + oneapi::tbb::filter f3(oneapi::tbb::filter_mode::serial_in_order, + MyOutputFunc(output_file) ); + oneapi::tbb::filter f = f1 & f2 & f3; + oneapi::tbb::parallel_pipeline(ntoken,f); + } + + +The input filter must be ``serial_in_order`` in this example because the +filter reads chunks from a sequential file and the output filter must +write the chunks in the same order. All ``serial_in_order`` filters +process items in the same order. Thus if an item arrives at +``MyOutputFunc`` out of the order established by ``MyInputFunc``, the +pipeline automatically delays invoking ``MyOutputFunc::operator()`` on +the item until its predecessors are processed. There is another kind of +serial filter, ``serial_out_of_order``, that does not preserve order. + + +The middle filter operates on purely local data. Thus any number of +invocations of its functor can run concurrently. Hence it is specified +as a parallel filter. + + +The functors for each filter are explained in detail now. The output +functor is the simplest. All it has to do is write a ``TextSlice`` to a +file and free the ``TextSlice``. + + +:: + + + // Functor that writes a TextSlice to a file. + class MyOutputFunc { + FILE* my_output_file; + public: + MyOutputFunc( FILE* output_file ); + void operator()( TextSlice* item ) const; + }; +   + + MyOutputFunc::MyOutputFunc( FILE* output_file ) : + my_output_file(output_file) + { + } +   + + void MyOutputFunc::operator()( TextSlice* out ) const { + size_t n = fwrite( out->begin(), 1, out->size(), my_output_file ); + if( n!=out->size() ) { + fprintf(stderr,"Can't write into file '%s'\n", OutputFileName); + exit(1); + } + out->free(); + } + + +Method ``operator()`` processes a ``TextSlice``. The parameter ``out`` +points to the ``TextSlice`` to be processed. Since it is used for the +last filter of the pipeline, it returns ``void``. + + +The functor for the middle filter is similar, but a bit more complex. It +returns a pointer to the ``TextSlice`` that it produces. + + +:: + + + // Functor that changes each decimal number to its square. + class MyTransformFunc { + public: + TextSlice* operator()( TextSlice* input ) const; + }; + + + TextSlice* MyTransformFunc::operator()( TextSlice* input ) const { + // Add terminating null so that strtol works right even if number is at end of the input. + *input->end() = '\0'; + char* p = input->begin(); + TextSlice* out = TextSlice::allocate( 2*MAX_CHAR_PER_INPUT_SLICE ); + char* q = out->begin(); + for(;;) { + while( pend() && !isdigit(*p) ) + *q++ = *p++; + if( p==input->end() ) + break; + long x = strtol( p, &p, 10 ); + // Note: no overflow checking is needed here, as we have twice the + // input string length, but the square of a non-negative integer n + // cannot have more than twice as many digits as n. + long y = x*x; + sprintf(q,"%ld",y); + q = strchr(q,0); + } + out->set_end(q); + input->free(); + return out; + } + + +The input functor is the most complicated, because it has to ensure that +no numeral crosses a boundary. When it finds what could be a numeral +crossing into the next slice, it copies the partial numeral to the next +slice. Furthermore, it has to indicate when the end of input is reached. +It does this by invoking method ``stop()`` on a special argument of type +``flow_control``. This idiom is required for any functor used for the +first filter of a pipline. + +:: + + + TextSlice* next_slice = NULL; + + + class MyInputFunc { + public: + MyInputFunc( FILE* input_file_ ); + MyInputFunc( const MyInputFunc& f ) : input_file(f.input_file) { } + ~MyInputFunc(); + TextSlice* operator()( oneapi::tbb::flow_control& fc ) const; + private: + FILE* input_file; + }; + + + MyInputFunc::MyInputFunc( FILE* input_file_ ) : + input_file(input_file_) { } + + + MyInputFunc::~MyInputFunc() { + } + + + TextSlice* MyInputFunc::operator()( oneapi::tbb::flow_control& fc ) const { + // Read characters into space that is available in the next slice. + if( !next_slice ) + next_slice = TextSlice::allocate( MAX_CHAR_PER_INPUT_SLICE ); + size_t m = next_slice->avail(); + size_t n = fread( next_slice->end(), 1, m, input_file ); + if( !n && next_slice->size()==0 ) { + // No more characters to process + fc.stop(); + return NULL; + } else { + // Have more characters to process. + TextSlice* t = next_slice; + next_slice = TextSlice::allocate( MAX_CHAR_PER_INPUT_SLICE ); + char* p = t->end()+n; + if( n==m ) { + // Might have read partial number. + // If so, transfer characters of partial number to next slice. + while( p>t->begin() && isdigit(p[-1]) ) + --p; + assert(p>t->begin(),"Number too large to fit in buffer.\n"); + next_slice->append( p, t->end()+n ); + } + t->set_end(p); + return t; + } + } + + +The copy constructor must be defined because the functor is copied when +the underlying ``oneapi::tbb::filter_t`` is built from the functor, and again when the pipeline runs. + + +.. |image0| image:: Images/image010.jpg + :width: 31px + :height: 26px +.. |image1| image:: Images/image010.jpg + :width: 31px + :height: 26px + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Using_Circular_Buffers + ../tbb_userguide/Throughput_of_pipeline + ../tbb_userguide/Non-Linear_Pipelines diff --git a/_sources/main/tbb_userguide/always_use_wait_for_all.rst b/_sources/main/tbb_userguide/always_use_wait_for_all.rst new file mode 100644 index 0000000000..be6f89fadb --- /dev/null +++ b/_sources/main/tbb_userguide/always_use_wait_for_all.rst @@ -0,0 +1,43 @@ +.. _always_use_wait_for_all: + +Always Use wait_for_all() +========================= + + +One of the most common mistakes made in flow graph programming is to +forget to call wait_for_all. The function graph::wait_for_all blocks +until all tasks spawned by the graph are complete. This is not only +useful when you want to wait until the computation is done, but it is +necessary to call wait_for_all before destroying the graph, or any of +its nodes. For example, the following function will lead to a program +failure: + + +:: + + + void no_wait_for_all() { + graph g; + function_node< int, int > f( g, 1, []( int i ) -> int { + return spin_for(i); + } ); + f.try_put(1); + + + // program will fail when f and g are destroyed at the + // end of the scope, since the body of f is not complete + } + + +In the function above, the graph g and its node f are destroyed at the +end of the function's scope. However, the task spawned to execute f's +body is still in flight. When the task completes, it will look for any +successors connected to its node, but by then both the graph and the +node have been deleted out from underneath it. Placing a +g.wait_for_all() at the end of the function prevents the premature +destruction of the graph and node. + + +If you use a flow graph and see mysterious behavior, check first to see +that you have called wait_for_all. + diff --git a/_sources/main/tbb_userguide/appendix_A.rst b/_sources/main/tbb_userguide/appendix_A.rst new file mode 100644 index 0000000000..2bd1e9c3b4 --- /dev/null +++ b/_sources/main/tbb_userguide/appendix_A.rst @@ -0,0 +1,48 @@ +.. _appendix_A: + +Appendix A Costs of Time Slicing +================================ + + +Time slicing enables there to be more logical threads than physical +threads. Each logical thread is serviced for a *time slice* by a +physical thread. If a thread runs longer than a time slice, as most do, +it relinquishes the physical thread until it gets another turn. This +appendix details the costs incurred by time slicing. + + +The most obvious is the time for *context switching* between logical +threads. Each context switch requires that the processor save all its +registers for the previous logical thread that it was executing, and +load its registers for the next logical thread that it runs. + + +A more subtle cost is *cache cooling*. Processors keep recently accessed +data in cache memory, which is very fast, but also relatively small +compared to main memory. When the processor runs out of cache memory, it +has to evict items from cache and put them back into main memory. +Typically, it chooses the least recently used items in the cache. (The +reality of set-associative caches is a bit more complicated, but this is +not a cache primer.) When a logical thread gets its time slice, as it +references a piece of data for the first time, this data will be pulled +into cache, taking hundreds of cycles. If it is referenced frequently +enough to not be evicted, each subsequent reference will find it in +cache, and only take a few cycles. Such data is called "hot in cache". +Time slicing undoes this, because if a thread A finishes its time slice, +and subsequently thread B runs on the same physical thread, B will tend +to evict data that was hot in cache for A, unless both threads need the +data. When thread A gets its next time slice, it will need to reload +evicted data, at the cost of hundreds of cycles for each cache miss. Or +worse yet, the next time slice for thread A may be on a different +physical thread that has a different cache altogether. + + +Another cost is *lock preemption.* This happens if a thread acquires a +lock on a resource, and its time slice runs out before it releases the +lock. No matter how short a time the thread intended to hold the lock, +it is now going to hold it for at least as long as it takes for its next +turn at a time slice to come up. Any other threads waiting on the lock +either pointlessly busy-wait, or lose the rest of their time slice. The +effect is called *convoying*, because the threads end up "bumper to +bumper" waiting for the preempted thread in front to resume driving. + diff --git a/_sources/main/tbb_userguide/appendix_B.rst b/_sources/main/tbb_userguide/appendix_B.rst new file mode 100644 index 0000000000..37c638b44e --- /dev/null +++ b/_sources/main/tbb_userguide/appendix_B.rst @@ -0,0 +1,74 @@ +.. _appendix_B: + +Appendix B Mixing With Other Threading Packages +=============================================== + + +|full_name| can be mixed with other +threading packages. No special effort is required to use any part of +oneTBB with other threading packages. + + +Here is an example that parallelizes an outer loop with OpenMP and an +inner loop with oneTBB. + + +:: + + + int M, N; +   + + struct InnerBody { + ... + }; +   + + void TBB_NestedInOpenMP() { + #pragma omp parallel + { + #pragma omp for + for( int i=0; i(0,N,10), InnerBody(i) ); + } + } + } + + +The details of ``InnerBody`` are omitted for brevity. The +``#pragma omp parallel`` causes the OpenMP to create a team of threads, +and each thread executes the block statement associated with the pragma. +The ``#pragma omp for`` indicates that the compiler should use the +previously created thread team to execute the loop in parallel. + + +Here is the same example written using POSIX\* Threads. + + +:: + + + int M, N; +   + + struct InnerBody { + ... + }; +   + + void* OuterLoopIteration( void* args ) { + int i = (int)args; + parallel_for( blocked_range(0,N,10), InnerBody(i) ); + } +   + + void TBB_NestedInPThreads() { + std::vector id( M ); + // Create thread for each outer loop iteration + for( int i=0; i src( g, [&]( oneapi::tbb::flow_control& fc ) -> int { + if ( src_count <= limit ) { + return src_count++; + } else { + fc.stop(); + return int(); + } + } ); + src.activate(); + + function_node< int, int > f( g, unlimited, [&]( int i ) -> int { + global_sum += i; // data race on global_sum + return i; + } ); + + + make_edge( src, f ); + g.wait_for_all(); + + + cout << "global sum = " << global_sum + << " and closed form = " << limit*(limit+1)/2 << "\n"; + + +If you run the above example, it will likely calculate a global sum that +is a bit smaller than the expected solution due to the data race. The +data race could be avoided in this simple example by changing the +allowed concurrency in ``f`` from unlimited to 1, forcing each value to be +processed sequentially by ``f``. You may also note that the ``input_node`` also +updates a global value, ``src_count``. However, since an ``input_node`` always +executes serially, there is no race possible. diff --git a/_sources/main/tbb_userguide/broadcast_or_send.rst b/_sources/main/tbb_userguide/broadcast_or_send.rst new file mode 100644 index 0000000000..68baa7540c --- /dev/null +++ b/_sources/main/tbb_userguide/broadcast_or_send.rst @@ -0,0 +1,134 @@ +.. _broadcast_or_send: + +Sending to One or Multiple Successors +===================================== + + +An important characteristic of the predefined nodes is whether they push +their output to a single successor or broadcast to all successors. The +following predefined nodes push messages to a single successor: + + +- buffer_node +- queue_node +- priority_queue_node +- sequencer_node + + +Other nodes push messages to all successors that will accept them. + + +The nodes that push to only a single successor are all buffer nodes. +Their purpose is to hold messages temporarily, until they are consumed +downstream. Consider the example below: + + +:: + + + void use_buffer_and_two_nodes() { + graph g; + + + function_node< int, int, rejecting > f1( g, 1, []( int i ) -> int { + spin_for(0.1); + cout << "f1 consuming " << i << "\n"; + return i; + } ); + + + function_node< int, int, rejecting > f2( g, 1, []( int i ) -> int { + spin_for(0.2); + cout << "f2 consuming " << i << "\n"; + return i; + } ); + + + priority_queue_node< int > q(g); + + + make_edge( q, f1 ); + make_edge( q, f2 ); + for ( int i = 10; i > 0; --i ) { + q.try_put( i ); + } + g.wait_for_all(); + } + + +First, function_nodes by default queue up the messages they receive at +their input. To make a priority_queue_node work properly with a +function_node, the example above constructs its function_nodes with its +buffer policy set to rejecting. So, f1 and f2 do not internally buffer +incoming messages, but instead rely on upstream buffering in the +priority_queue_node. + + +In the above example, each message buffered by the priority_queue_node +is sent to either f1 or f2, but not both. + + +Let's consider the alternative behavior; that is; what if the +priority_queue_node broadcasts to all successors. What if some, but not +all, nodes accept a message? Should the message be buffered until all +nodes accept it, or be only delivered to the accepting subset? If the +node continues to buffer the message, should it eventually deliver the +messages in the same order to all nodes or in the current priority order +at the time the node accepts the next message? For example, assume a +priority_queue_node only contains "9" when a successor node, f1, accepts +"9" but another successor node, f2, rejects it. Later a value "100" +arrives and f2 is available to accept messages. Should f2 receive "9" +next or "100", which has a higher priority? In any case, trying to +ensure that all successors receive each message creates a garbage +collection problem and complicates reasoning. Therefore, these buffering +nodes push each message to only one successor. And, you can use this +characteristic to create useful graph structures such as the one shown +in the graph above, where each message will be processed in priority +order, by either f1 or f2. + + +But what if you really do want both f1 and f2 to receive all of the +values, and in priority order? You can easily create this behavior by +creating one priority_queue_node for each function_node, and pushing +each value to both queues through a broadcast_node, as shown below: + + +:: + + + graph g; + + + function_node< int, int, rejecting > f1( g, 1, []( int i ) -> int { + spin_for(0.1); + cout << "f1 consuming " << i << "\n"; + return i; + } ); + + + function_node< int, int, rejecting > f2( g, 1, []( int i ) -> int { + spin_for(0.2); + cout << "f2 consuming " << i << "\n"; + return i; + } ); + + + priority_queue_node< int > q1(g); + priority_queue_node< int > q2(g); + broadcast_node< int > b(g); + + + make_edge( b, q1 ); + make_edge( b, q2 ); + make_edge( q1, f1 ); + make_edge( q2, f2 ); + for ( int i = 10; i > 0; --i ) { + b.try_put( i ); + } + g.wait_for_all(); + + +So, when connecting a node in your graph to multiple successors, be sure +to understand whether the output will broadcast to all of the +successors, or just a single successor. + diff --git a/_sources/main/tbb_userguide/cancel_a_graph.rst b/_sources/main/tbb_userguide/cancel_a_graph.rst new file mode 100644 index 0000000000..4ae598d7ea --- /dev/null +++ b/_sources/main/tbb_userguide/cancel_a_graph.rst @@ -0,0 +1,86 @@ +.. _cancel_a_graph: + +Cancel a Graph Explicitly +========================= + + +To cancel a graph execution without an exception, you can create the +graph using an explicit task_group_context, and then call +cancel_group_execution() on that object. This is done in the example +below: + + +:: + + + task_group_context t; + graph g(t); + + + function_node< int, int > f1( g, 1, []( int i ) { return i; } ); + + + function_node< int, int > f2( g, 1, + []( const int i ) -> int { + cout << "Begin " << i << "\n"; + spin_for(0.2); + cout << "End " << i << "\n"; + return i; + } ); + + + function_node< int, int > f3( g, 1, []( int i ) { return i; } ); + + + make_edge( f1, f2 ); + make_edge( f2, f3 ); + f1.try_put(1); + f1.try_put(2); + spin_for(0.1); + t.cancel_group_execution(); + g.wait_for_all(); + + +When a graph execution is canceled, any node that has already started to +execute will execute to completion, but any node that has not started to +execute will not start. So in the example above, f2 will print both the +Begin and End message for input 1, but will not receive the input 2. + + +You can also get the task_group_context that a node belongs to from +within the node body and use it to cancel the execution of the graph it +belongs to: + + +:: + + + graph g; + + + function_node< int, int > f1( g, 1, []( int i ) { return i; } ); + + + function_node< int, int > f2( g, 1, + []( const int i ) -> int { + cout << "Begin " << i << "\n"; + spin_for(0.2); + cout << "End " << i << "\n"; + task::self().group()->cancel_group_execution(); + return i; + } ); + + + function_node< int, int > f3( g, 1, []( int i ) { return i; } ); + + + make_edge( f1, f2 ); + make_edge( f2, f3 ); + f1.try_put(1); + f1.try_put(2); + g.wait_for_all(); + + +You can get the task_group_context from a node's body even if the graph +was not explicitly passed one at construction time. + diff --git a/_sources/main/tbb_userguide/cancelling_nested_parallelism.rst b/_sources/main/tbb_userguide/cancelling_nested_parallelism.rst new file mode 100644 index 0000000000..6fc3ed158b --- /dev/null +++ b/_sources/main/tbb_userguide/cancelling_nested_parallelism.rst @@ -0,0 +1,21 @@ +.. _cancelling_nested_parallelism: + +Canceling Nested Parallelism +============================ + + +Nested parallelism is canceled if the inner context is bound to the +outer context; otherwise it is not. + + +If the execution of a flow graph is canceled, either explicitly or due +to an exception, any tasks started by parallel algorithms or flow graphs +nested within the nodes of the canceled flow graph may or may not be +canceled. + + +As with all of the library's nested parallelism, you can control +cancellation relationships by use of explicit task_group_context +objects. If you do not provide an explicit task_group_context to a flow +graph, it is created with an isolated context by default. + diff --git a/_sources/main/tbb_userguide/catching_exceptions.rst b/_sources/main/tbb_userguide/catching_exceptions.rst new file mode 100644 index 0000000000..17878954ee --- /dev/null +++ b/_sources/main/tbb_userguide/catching_exceptions.rst @@ -0,0 +1,80 @@ +.. _catching_exceptions: + +Catching Exceptions Inside the Node that Throws the Exception +============================================================= + + +If you catch an exception within the node's body, execution continues +normally, as you might expect. If an exception is thrown but is not +caught before it propagates beyond the node's body, the execution of all +of the graph's nodes are canceled and the exception is rethrown at the +call site of graph::wait_for_all(). Take the graph below as an example: + + +:: + + + graph g; + + + function_node< int, int > f1( g, 1, []( int i ) { return i; } ); + + + function_node< int, int > f2( g, 1, + []( const int i ) -> int { + throw i; + return i; + } ); + + + function_node< int, int > f3( g, 1, []( int i ) { return i; } ); + + + make_edge( f1, f2 ); + make_edge( f2, f3 ); + f1.try_put(1); + f1.try_put(2); + g.wait_for_all(); + + +In the code above, the second function_node, f2, throws an exception +that is not caught within the body. This will cause the execution of the +graph to be canceled and the exception to be rethrown at the call to +g.wait_for_all(). Since it is not handled there either, the program will +terminate. If desirable, the exception could be caught and handled +within the body: + + +:: + + + function_node< int, int > f2( g, 1, + []( const int i ) -> int { + try { + throw i; + } catch (int j) { + cout << "Caught " << j << "\n"; + } + return i; + } ); + + +If the exception is caught and handled in the body, then there is no +effect on the overall execution of the graph. However, you could choose +instead to catch the exception at the call to wait_for_all: + + +:: + + + try { + g.wait_for_all(); + } catch ( int j ) { + cout << "Caught " << j << "\n"; + } + + +In this case, the execution of the graph is canceled. For our example, +this means that the input 1 never reaches f3 and that input 2 never +reaches either f2 or f3. + diff --git a/_sources/main/tbb_userguide/communicate_with_nodes.rst b/_sources/main/tbb_userguide/communicate_with_nodes.rst new file mode 100644 index 0000000000..062c35bb57 --- /dev/null +++ b/_sources/main/tbb_userguide/communicate_with_nodes.rst @@ -0,0 +1,106 @@ +.. _communicate_with_nodes: + +Communication Between Graphs +============================ + + +All graph nodes require a reference to a graph object as one of the +arguments to their constructor. It is only safe to construct edges +between nodes that are part of the same graph. An edge expresses the +topology of your graph to the runtime library. Connecting two nodes in +different graphs can make it difficult to reason about whole graph +operations, such as calls to graph::wait_for_all and exception handling. +To optimize performance, the library may make calls to a node's +predecessor or successor at times that are unexpected by the user. + + +If two graphs must communicate, do NOT create an edge between them, but +instead use explicit calls to try_put. This will prevent the runtime +library from making any assumptions about the relationship of the two +nodes, and therefore make it easier to reason about events that cross +the graph boundaries. However, it may still be difficult to reason about +whole graph operations. For example, consider the graphs below: + + +:: + + + graph g; + function_node< int, int > n1( g, 1, [](int i) -> int { + cout << "n1\n"; + spin_for(i); + return i; + } ); + function_node< int, int > n2( g, 1, [](int i) -> int { + cout << "n2\n"; + spin_for(i); + return i; + } ); + make_edge( n1, n2 ); + + + graph g2; + function_node< int, int > m1( g2, 1, [](int i) -> int { + cout << "m1\n"; + spin_for(i); + return i; + } ); + function_node< int, int > m2( g2, 1, [&](int i) -> int { + cout << "m2\n"; + spin_for(i); + n1.try_put(i); + return i; + } ); + make_edge( m1, m2 ); + + + m1.try_put( 1 ); + + + // The following call returns immediately: + g.wait_for_all(); + // The following call returns after m1 & m2 + g2.wait_for_all(); + + + // we reach here before n1 & n2 are finished + // even though wait_for_all was called on both graphs + + +In the example above, m1.try_put(1) sends a message to node m1, which +runs its body and then sends a message to node m2. Next, node m2 runs +its body and sends a message to n1 using an explicit try_put. In turn, +n1 runs its body and sends a message to n2. The runtime library does not +consider m2 to be a predecessor of n1 since no edge exists. + + +If you want to wait until all of the tasks spawned by these graphs are +done, you need to call the function wait_for_all on both graphs. +However, because there is cross-graph communication, the order of the +calls is important. In the (incorrect) code segment above, the first +call to g.wait_for_all() returns immediately because there are no tasks +yet active in g; the only tasks that have been spawned by then belong to +g2. The call to g2.wait_for_all returns after both m1 and m2 are done, +since they belong to g2; the call does not however wait for n1 and n2, +since they belong to g. The end of this code segment is therefore +reached before n1 and n2 are done. + + +If the calls to wait_for_all are swapped, the code works as expected: + + +:: + + + g2.wait_for_all(); + g.wait_for_all(); + + + // all tasks are done + + +While it is not too difficult to reason about how these two very small +graphs interact, the interaction of two larger graphs, perhaps with +cycles, will be more difficult to understand. Therefore, communication +between nodes in different graphs should be done with caution. + diff --git a/_sources/main/tbb_userguide/concurrent_hash_map.rst b/_sources/main/tbb_userguide/concurrent_hash_map.rst new file mode 100644 index 0000000000..8d9ba3a1b7 --- /dev/null +++ b/_sources/main/tbb_userguide/concurrent_hash_map.rst @@ -0,0 +1,131 @@ +.. _concurrent_hash_map: + +concurrent_hash_map +=================== + + +A ``concurrent_hash_map`` is a hash table that +permits concurrent accesses. The table is a map from a key to a type +``T``. The traits type HashCompare defines how to hash a key and how to +compare two keys. + + +The following example builds a ``concurrent_hash_map`` where the keys +are strings and the corresponding data is the number of times each +string occurs in the array ``Data``. + + +:: + + + #include "oneapi/tbb/concurrent_hash_map.h" + #include "oneapi/tbb/blocked_range.h" + #include "oneapi/tbb/parallel_for.h" + #include +   + + using namespace oneapi::tbb; + using namespace std; +   + + // Structure that defines hashing and comparison operations for user's type. + struct MyHashCompare { + size_t hash( const string& x ) const { + size_t h = 0; + for( const char* s = x.c_str(); *s; ++s ) + h = (h*17)^*s; + return h; + } + //! True if strings are equal + bool equal( const string& x, const string& y ) const { + return x==y; + } + }; +   + + // A concurrent hash table that maps strings to ints. + typedef concurrent_hash_map StringTable; +   + + // Function object for counting occurrences of strings. + struct Tally { + StringTable& table; + Tally( StringTable& table_ ) : table(table_) {} + void operator()( const blocked_range range ) const { + for( string* p=range.begin(); p!=range.end(); ++p ) { + StringTable::accessor a; + table.insert( a, *p ); + a->second += 1; + } + } + }; +   + + const size_t N = 1000000; +   + + string Data[N]; +   + + void CountOccurrences() { + // Construct empty table. + StringTable table; +   + + // Put occurrences into the table + parallel_for( blocked_range( Data, Data+N, 1000 ), + Tally(table) ); +   + + // Display the occurrences + for( StringTable::iterator i=table.begin(); i!=table.end(); ++i ) + printf("%s %d\n",i->first.c_str(),i->second); + } + + +A ``concurrent_hash_map`` acts as a container of elements of type +``std::pair``. Typically, when accessing a container +element, you are interested in either updating it or reading it. The +template class ``concurrent_hash_map`` supports these two purposes +respectively with the classes ``accessor`` and ``const_accessor`` that +act as smart pointers. An *accessor* represents *update* (*write*) +access. As long as it points to an element, all other attempts to look +up that key in the table block until the ``accessor`` is done. A +``const_accessor`` is similar, except that is represents *read-only* +access. Multiple ``const_accessors`` can point to the same element at +the same time. This feature can greatly improve concurrency in +situations where elements are frequently read and infrequently updated. + + +The methods ``find`` and ``insert`` take an ``accessor`` or +``const_accessor`` as an argument. The choice tells +``concurrent_hash_map`` whether you are asking for *update* or +*read-only* access. Once the method returns, the access lasts until the +``accessor`` or ``const_accessor`` is destroyed. Because having access +to an element can block other threads, try to shorten the lifetime of +the ``accessor`` or ``const_accessor``. To do so, declare it in the +innermost block possible. To release access even sooner than the end of +the block, use method ``release``. The following example is a rework of +the loop body that uses ``release`` instead of depending upon +destruction to end thread lifetime: + + +:: + + + StringTable accessor a; + for( string* p=range.begin(); p!=range.end(); ++p ) { + table.insert( a, *p ); + a->second += 1; + a.release(); + } + + +The method ``remove(key)`` can also operate concurrently. It implicitly +requests write access. Therefore before removing the key, it waits on +any other extant accesses on ``key``. + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/More_on_HashCompare diff --git a/_sources/main/tbb_userguide/concurrent_vector_ug.rst b/_sources/main/tbb_userguide/concurrent_vector_ug.rst new file mode 100644 index 0000000000..2c1f84b253 --- /dev/null +++ b/_sources/main/tbb_userguide/concurrent_vector_ug.rst @@ -0,0 +1,62 @@ +.. _concurrent_vector_ug: + +concurrent_vector +================= + + +``A concurrent_vector`` is a dynamically growable array of ``T``. It +is safe to grow a ``concurrent_vector`` while other threads are also +operating on elements of it, or even growing it themselves. For safe +concurrent growing, ``concurrent_vector`` has three methods that support +common uses of dynamic arrays: ``push_back``, ``grow_by``, and +``grow_to_at_least``. + + +Method ``push_back(x)`` safely appends x to the array. Method +``grow_by(n)`` safely appends ``n`` consecutive elements initialized +with ``T()``. Both methods return an iterator pointing to the first +appended element. Each element is initialized with ``T()``. So for +example, the following routine safely appends a C string to a shared +vector: + + +:: + + + void Append( concurrent_vector& vector, const char* string ) { + size_t n = strlen(string)+1; + std::copy( string, string+n, vector.grow_by(n) ); + } + + +The related method ``grow_to_at_least(n)``\ grows a vector to size ``n`` +if it is shorter. Concurrent calls to the growth methods do not +necessarily return in the order that elements are appended to the +vector. + + +Method ``size()`` returns the number of elements in the vector, which +may include elements that are still undergoing concurrent construction +by methods ``push_back``, ``grow_by,`` or ``grow_to_at_least``. The +example uses std::copy and iterators, not ``strcpy and pointers``, +because elements in a ``concurrent_vector`` might not be at consecutive +addresses. It is safe to use the iterators while the +``concurrent_vector`` is being grown, as long as the iterators never go +past the current value of ``end()``. However, the iterator may reference +an element undergoing concurrent construction. You must synchronize +construction and access. + + +A ``concurrent_vector`` never moves an element until the array is +cleared, which can be an advantage over the STL std::vector even for +single-threaded code. However, ``concurrent_vector`` does have more +overhead than std::vector. Use ``concurrent_vector`` only if you really +need the ability to dynamically resize it while other accesses are (or +might be) in flight, or require that an element never move. + + +.. CAUTION:: + Operations on ``concurrent_vector`` are concurrency safe with respect + to *growing*, not for clearing or destroying a vector. Never invoke + method ``clear()`` if there are other operations in flight on the + ``concurrent_vector``. diff --git a/_sources/main/tbb_userguide/create_token_based_system.rst b/_sources/main/tbb_userguide/create_token_based_system.rst new file mode 100644 index 0000000000..6d9d642556 --- /dev/null +++ b/_sources/main/tbb_userguide/create_token_based_system.rst @@ -0,0 +1,144 @@ +.. _create_token_based_system: + +Create a Token-Based System +=========================== + + +A more flexible solution to limit the number of messages in a flow graph +is to use tokens. In a token-based system, a limited number of tokens +are available in the graph and a message will not be allowed to enter +the graph until it can be paired with an available token. When a message +is retired from the graph, its token is released, and can be paired with +a new message that will then be allowed to enter. + + +The ``oneapi::tbb::parallel_pipeline`` algorithm relies on a token-based system. In +the flow graph interface, there is no explicit support for tokens, but +``join_node``s can be used to create an analogous system. A ``join_node`` has +two template arguments, the tuple that describes the types of its inputs +and a buffer policy: + + +:: + + + template + class join_node; + + +The buffer policy can be one of the following: + + +- ``queueing``. This type of policy causes inputs to be matched + first-in-first-out; that is, the inputs are joined together to form a + tuple in the order they are received. +- ``tag_matching``. This type of policy joins inputs together that have + matching tags. +- ``reserving``. This type of policy causes the ``join_node`` to do no + internally buffering, but instead to consume inputs only when it can + first reserve an input on each port from an upstream source. If it + can reserve an input at each port, it gets those inputs and joins + those together to form an output tuple. + + +A token-based system can be created by using reserving join_nodes. + + +In the example below, there is an ``input_node`` that generates ``M`` big +objects and a ``buffer_node`` that is pre-filled with three tokens. The +``token_t`` can be anything, for example it could be ``typedef int token_t;``. +The ``input_node`` and ``buffer_node`` are connected to a reserving ``join_node``. +The ``input_node`` will only generate an input when one is pulled from it +by the reserving ``join_node``, and the reserving ``join_node`` will only pull +the input from the ``input_node`` when it knows there is also an item to +pull from the ``buffer_node``. + + +:: + + + graph g; + + + int src_count = 0; + int number_of_objects = 0; + int max_objects = 3; + + + input_node< big_object * > s( g, [&]( oneapi::tbb::flow_control& fc ) -> big_object* { + if ( src_count < M ) { + big_object* v = new big_object(); + ++src_count; + return v; + } else { + fc.stop(); + return nullptr; + } + } ); + s.activate(); + + join_node< tuple_t, reserving > j(g); + + + buffer_node< token_t > b(g); + + + function_node< tuple_t, token_t > f( g, unlimited, + []( const tuple_t &t ) -> token_t { + spin_for(1); + cout << get<1>(t) << "\n"; + delete get<0>(t); + return get<1>(t); + } ); + + + make_edge( s, input_port<0>(j) ); + make_edge( b, input_port<1>(j) ); + make_edge( j, f ); + make_edge( f, b ); + + + b.try_put( 1 ); + b.try_put( 2 ); + b.try_put( 3 ); + + + g.wait_for_all(); + + +In the above code, you can see that the ``function_node`` returns the token +back to the ``buffer_node``. This cycle in the flow graph allows the token +to be recycled and paired with another input from the ``input_node``. So +like in the previous sections, there will be at most four big objects in +the graph. There could be three big objects in the ``function_node`` and one +buffered in the ``input_node``, awaiting a token to be paired with. + + +Since there is no specific ``token_t`` defined for the flow graph, you can +use any type for a token, including objects or pointers to arrays. +Therefore, unlike in the example above, the ``token_t`` doesn't need to be a +dummy type; it could for example be a buffer or other object that is +essential to the computation. We could, for example, modify the example +above to use the big objects themselves as the tokens, removing the need +to repeatedly allocate and deallocate them, and essentially create a +free list of big objects using a cycle back to the ``buffer_node``. + + +Also, in our example above, the ``buffer_node`` was prefilled by a fixed +number of explicit calls to ``try_put``, but there are other options. For +example, an ``input_node`` could be attached to the input of the +``buffer_node``, and it could generate the tokens. In addition, our +``function_node`` could be replaced by a ``multifunction_node`` that can +optionally put 0 or more outputs to each of its output ports. Using a +``multifunction_node``, you can choose to recycle or not recycle a token, or +even generate more tokens, thereby increasing or decreasing the allowed +concurrency in the graph. + + +A token based system is therefore very flexible. You are free to declare +the token to be of any type and to inject or remove tokens from the +system as it is executing, thereby having dynamic control of the allowed +concurrency in the system. Since you can pair the token with an input at +the source, this approach enables you to limit resource consumption +across the entire graph. + diff --git a/_sources/main/tbb_userguide/design_patterns/Agglomeration.rst b/_sources/main/tbb_userguide/design_patterns/Agglomeration.rst new file mode 100644 index 0000000000..72fbd11c8c --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Agglomeration.rst @@ -0,0 +1,171 @@ +.. _Agglomeration: + +Agglomeration +============= + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Parallelism is so fine grained that overhead of parallel scheduling + or communication swamps the useful work. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Many algorithms permit parallelism at a very fine grain, on the order + of a few instructions per task. But synchronization between threads + usually requires orders of magnitude more cycles. For example, + elementwise addition of two arrays can be done fully in parallel, but + if each scalar addition is scheduled as a separate task, most of the + time will be spent doing synchronization instead of useful addition. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Individual computations can be done in parallel, but are small. + For practical use of |full_name|, + "small" here means less than 10,000 clock cycles. + + + - The parallelism is for sake of performance and not required for + semantic reasons. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Group the computations into blocks. Evaluate computations within a + block serially. + + + The block size should be chosen to be large enough to amortize + parallel overhead. Too large a block size may limit parallelism or + load balancing because the number of blocks becomes too small to + distribute work evenly across processors. + + + The choice of block topology is typically driven by two concerns: + + + - Minimizing synchronization between blocks. + + + - Minimizing cache traffic between blocks. + + + If the computations are completely independent, then the blocks will + be independent too, and then only cache traffic issues must be + considered. + + + If the loop is "small", on the order of less than 10,000 clock + cycles, then it may be impractical to parallelize at all, because the + optimal agglomeration might be a single block, + + +.. container:: section + + + .. rubric:: Examples + :class: sectiontitle + + TBB loop templates such as ``oneapi::tbb::parallel_for`` that take a *range* + argument support automatic agglomeration. + + + When agglomerating, think about cache effects. Avoid having cache + lines cross between groups if possible. + + + There may be boundary to interior ratio effects. For example, if the + computations form a 2D grid, and communicate only with nearest + neighbors, then the computation per block grows quadratically (with + the block's area), but the cross-block communication grows with + linearly (with the block's perimeter). The following figure shows + four different ways to agglomerate an 8×8 grid. If doing such + analysis, be careful to consider that information is transferred in + cache line units. For a given area, the perimeter may be minimized + when the block is square with respect to the underlying grid of cache + lines, not square with respect to the logical grid. + + + .. container:: fignone + :name: fig1 + + + Four different agglomerations of an 8×8 grid. |image0| + + + Also consider vectorization. Blocks that contain long contiguous + subsets of data may better enable vectorization. + + + For recursive computations, most of the work is towards the leaves, + so the solution is to treat subtrees as a groups as shown in the + following figure. + + + .. container:: fignone + :name: fig2 + + + Agglomeration of a recursive computation |image1| + + + Often such an agglomeration is achieved by recursing serially once + some threshold is reached. For example, a recursive sort might solve + sub-problems in parallel only if they are above a certain threshold + size. + + +.. container:: section + + + .. rubric:: Reference + :class: sectiontitle + + Ian Foster introduced the term "agglomeration" in his book Designing + and Building Parallel Programs http://www.mcs.anl.gov/~itf/dbpp. + There agglomeration is part of a four step **PCAM** design method: + + + #. **P**\ artitioning - break the program into the smallest tasks + possible. + + + #. **C**\ ommunication – figure out what communication is required + between tasks. When using oneTBB, communication is usually cache + line transfers. Though they are automatic, understanding which + ones happen between tasks helps guide the agglomeration step. + + + #. **A**\ gglomeration – combine tasks into larger tasks. His book + has an extensive list of considerations that is worth reading. + + + #. **M**\ apping – map tasks onto processors. The oneTBB task + scheduler does this step for you. + + +.. |image0| image:: Images/image002a.jpg + :width: 301px + :height: 293px +.. |image1| image:: Images/image003a.jpg + :width: 291px + :height: 150px + diff --git a/_sources/main/tbb_userguide/design_patterns/Design_Patterns.rst b/_sources/main/tbb_userguide/design_patterns/Design_Patterns.rst new file mode 100644 index 0000000000..5daa696b5b --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Design_Patterns.rst @@ -0,0 +1,58 @@ +.. _design_patterns: + +Design Patterns +=============== + + +This section provides some common parallel programming patterns and how +to implement them in |full_name|. + + +The description of each pattern has the following format: + + +- **Problem** – describes the problem to be solved. + + +- **Context** – describes contexts in which the problem arises. + + +- **Forces** - considerations that drive use of the pattern. + + +- **Solution** - describes how to implement the pattern. + + +- **Example** – presents an example implementation. + + +Variations and examples are sometimes discussed. The code examples are +intended to emphasize key points and are not full-fledged code. Examples +may omit obvious const overloads of non-const methods. + + +Much of the nomenclature and examples are adapted from Web pages created +by Eun-Gyu and Marc Snir, and the Berkeley parallel patterns wiki. See +links in the **General References** section. + + +For brevity, some of the code examples use C++11 lambda expressions. It +is straightforward, albeit sometimes tedious, to translate such lambda +expressions into equivalent C++03 code. + +.. toctree:: + :maxdepth: 4 + + ../../tbb_userguide/design_patterns/Agglomeration + ../../tbb_userguide/design_patterns/Elementwise + ../../tbb_userguide/design_patterns/Odd-Even_Communication + ../../tbb_userguide/design_patterns/Wavefront + ../../tbb_userguide/design_patterns/Reduction + ../../tbb_userguide/design_patterns/Divide_and_Conquer + ../../tbb_userguide/design_patterns/GUI_Thread + ../../tbb_userguide/design_patterns/Non-Preemptive_Priorities + ../../tbb_userguide/design_patterns/Lazy_Initialization + ../../tbb_userguide/design_patterns/Local_Serializer + ../../tbb_userguide/design_patterns/Fenced_Data_Transfer + ../../tbb_userguide/design_patterns/Reference_Counting + ../../tbb_userguide/design_patterns/General_References diff --git a/_sources/main/tbb_userguide/design_patterns/Divide_and_Conquer.rst b/_sources/main/tbb_userguide/design_patterns/Divide_and_Conquer.rst new file mode 100644 index 0000000000..f09787ea21 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Divide_and_Conquer.rst @@ -0,0 +1,217 @@ +.. _Divide_and_Conquer: + +Divide and Conquer +================== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Parallelize a divide and conquer algorithm. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Divide and conquer is widely used in serial algorithms. Common + examples are quicksort and mergesort. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Problem can be transformed into subproblems that can be solved + independently. + + + - Splitting problem or merging solutions is relatively cheap + compared to cost of solving the subproblems. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + There are several ways to implement divide and conquer in + |full_name|. The best choice depends upon circumstances. + + + - If division always yields the same number of subproblems, use + recursion and ``oneapi::tbb::parallel_invoke``. + + + - If the number of subproblems varies, use recursion and + ``oneapi::tbb::task_group``. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + Quicksort is a classic divide-and-conquer algorithm. It divides a + sorting problem into two subsorts. A simple serial version looks like [1]_. + + + :: + + + void SerialQuicksort( T* begin, T* end ) { + if( end-begin>1 ) { + using namespace std; + T* mid = partition( begin+1, end, bind2nd(less(),*begin) ); + swap( *begin, mid[-1] ); + SerialQuicksort( begin, mid-1 ); + SerialQuicksort( mid, end ); + } + } + + + The number of subsorts is fixed at two, so ``oneapi::tbb::parallel_invoke`` + provides a simple way to parallelize it. The parallel code is shown + below: + + + :: + + + void ParallelQuicksort( T* begin, T* end ) { + if( end-begin>1 ) { + using namespace std; + T* mid = partition( begin+1, end, bind2nd(less(),*begin) ); + swap( *begin, mid[-1] ); + oneapi::tbb::parallel_invoke( [=]{ParallelQuicksort( begin, mid-1 );}, + [=]{ParallelQuicksort( mid, end );} ); + } + } + + + Eventually the subsorts become small enough that serial execution is + more efficient. The following variation, does sorts of less than 500 elements using the earlier serial code. + + + :: + + + void ParallelQuicksort( T* begin, T* end ) { + if( end-begin>=500 ) { + using namespace std; + T* mid = partition( begin+1, end, bind2nd(less(),*begin) ); + swap( *begin, mid[-1] ); + oneapi::tbb::parallel_invoke( [=]{ParallelQuicksort( begin, mid-1 );}, + [=]{ParallelQuicksort( mid, end );} ); + } else { + SerialQuicksort( begin, end ); + } + } + + + The change is an instance of the Agglomeration pattern. + + + The next example considers a problem where there are a variable + number of subproblems. The problem involves a tree-like description + of a mechanical assembly. There are two kinds of nodes: + + + - Leaf nodes represent individual parts. + + + - Internal nodes represent groups of parts. + + + The problem is to find all nodes that collide with a target node. The + following code shows a serial solution that walks the tree. It + records in ``Hits`` any nodes that collide with ``Target``. + + + :: + + + std::list Hits; + Node* Target; +   + + void SerialFindCollisions( Node& x ) { + if( x.is_leaf() ) { + if( x.collides_with( *Target ) ) + Hits.push_back(&x); + } else { + for( Node::const_iterator y=x.begin();y!=x.end(); ++y ) + SerialFindCollisions(*y); + } + } + + + A parallel version is shown below. + + + :: + + + typedef oneapi::tbb::enumerable_thread_specific > LocalList; + LocalList LocalHits; + Node* Target; // Target node +   + + void ParallelWalk( Node& x ) { + if( x.is_leaf() ) { + if( x.collides_with( *Target ) ) + LocalHits.local().push_back(&x); + } else { + // Recurse on each child y of x in parallel + oneapi::tbb::task_group g; + for( Node::const_iterator y=x.begin(); y!=x.end(); ++y ) + g.run( [=]{ParallelWalk(*y);} ); + // Wait for recursive calls to complete + g.wait(); + } + } +   + + void ParallelFindCollisions( Node& x ) { + ParallelWalk(x); + for(LocalList::iterator i=LocalHits.begin();i!=LocalHits.end(); ++i) + Hits.splice( Hits.end(), *i ); + } + + + The recursive walk is parallelized using class ``task_group`` to do + recursive calls in parallel. + + + There is another significant change because of the parallelism that + is introduced. Because it would be unsafe to update ``Hits`` + concurrently, the parallel walk uses variable ``LocalHits`` to + accumulate results. Because it is of type + ``enumerable_thread_specific``, each thread accumulates its own + private result. The results are spliced together into Hits after the + walk completes. + + + The results will *not* be in the same order as the original serial + code. + + + If parallel overhead is high, use the agglomeration pattern. For + example, use the serial walk for subtrees under a certain threshold. + + +.. [1] Production quality quicksort implementations typically + use more sophisticated pivot selection, explicit stacks instead of + recursion, and some other sorting algorithm for small subsorts. The + simple algorithm is used here to focus on exposition of the parallel + pattern. + diff --git a/_sources/main/tbb_userguide/design_patterns/Elementwise.rst b/_sources/main/tbb_userguide/design_patterns/Elementwise.rst new file mode 100644 index 0000000000..6e5e48b364 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Elementwise.rst @@ -0,0 +1,138 @@ +.. _Elementwise: + +Elementwise +=========== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Initiate similar independent computations across items in a data set, + and wait until all complete. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Many serial algorithms sweep over a set of items and do an + independent computation on each item. However, if some kind of + summary information is collected, use the Reduction pattern instead. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + No information is carried or merged between the computations. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + If the number of items is known in advance, use + ``oneapi::tbb::parallel_for``. If not, consider using + ``oneapi::tbb::parallel_for_each``. + + + Use agglomeration if the individual computations are small relative + to scheduler overheads. + + + If the pattern is followed by a reduction on the same data, consider + doing the element-wise operation as part of the reduction, so that + the combination of the two patterns is accomplished in a single sweep + instead of two sweeps. Doing so may improve performance by reducing + traffic through the memory hierarchy. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + Convolution is often used in signal processing. The convolution of a + filter ``c`` and signal ``x`` is computed as: + + + |image0| + Serial code for this computation might look like: + + + :: + + + // Assumes c[0..clen-1] and x[1-clen..xlen-1] are defined + for( int i=0; i(0,xlen+clen-1,1000), + [=]( oneapi::tbb::blocked_range r ) { + int end = r.end(); + for( int i=r.begin(); i!=end; ++i ) { + float tmp = 0; + for( int j=0; j`` for the flag + that indicates when the message is ready. Here is the previous + example with modifications. + + + :: + + + std::atomic Ready; + std::string Message; +   + + void Send( const std::string& src ) {. // Executed by thread 1 + Message=src; + Ready.store(true, std::memory_order_release); + } +   + + bool Receive( std::string& dst ) { // Executed by thread 2 + bool result = Ready.load(std::memory_order_acquire); + if( result ) dst=Message; + return result; // Return true if message was received. + } + + + A write to a ``std::atomic`` value has *release* semantics, which + means that all of its prior writes will be seen before the releasing + write. A read from ``std::atomic`` value has *acquire* semantics, + which means that all of its subsequent reads will happen after the + acquiring read. The implementation of ``std::atomic`` ensures that + both the compiler and the hardware observe these ordering + constraints. + + +.. container:: section + + + .. rubric:: Variations + :class: sectiontitle + + Higher level synchronization constructs normally include the + necessary *acquire* and *release* fences. For example, mutexes are + normally implemented such that acquisition of a lock has *acquire* + semantics and release of a lock has *release* semantics. Thus a + thread that acquires a lock on a mutex always sees any memory writes + done by another thread before it released a lock on that mutex. + + +.. container:: section + + + .. rubric:: Non Solutions + :class: sectiontitle + + Mistaken solutions are so often proposed that it is worth + understanding why they are wrong. + + + One common mistake is to assume that declaring the flag with the + ``volatile`` keyword solves the problem. Though the ``volatile`` + keyword forces a write to happen immediately, it generally has no + effect on the visible ordering of that write with respect to other + memory operations. + + + Another mistake is to assume that conditionally executed code cannot + happen before the condition is tested. However, the compiler or + hardware may speculatively hoist the conditional code above the + condition. + + + Similarly, it is a mistake to assume that a processor cannot read the + target of a pointer before reading the pointer. A modern processor + does not read individual values from main memory. It reads cache + lines. The target of a pointer may be in a cache line that has + already been read before the pointer was read, thus giving the + appearance that the processor presciently read the pointer target. + diff --git a/_sources/main/tbb_userguide/design_patterns/GUI_Thread.rst b/_sources/main/tbb_userguide/design_patterns/GUI_Thread.rst new file mode 100644 index 0000000000..b837468c71 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/GUI_Thread.rst @@ -0,0 +1,205 @@ +.. _GUI_Thread: + +GUI Thread +========== + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + A user interface thread must remain responsive to user requests, and + must not get bogged down in long computations. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Graphical user interfaces often have a dedicated thread ("GUI + thread") for servicing user interactions. The thread must remain + responsive to user requests even while the application has long + computations running. For example, the user might want to press a + "cancel" button to stop the long running computation. If the GUI + thread takes part in the long running computation, it will not be + able to respond to user requests. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - The GUI thread services an event loop. + + + - The GUI thread needs to offload work onto other threads without + waiting for the work to complete. + + + - The GUI thread must be responsive to the event loop and not become + dedicated to doing the offloaded work. + + +.. container:: section + + + .. rubric:: Related + :class: sectiontitle + + - Non-Preemptive Priorities + - Local Serializer + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + The GUI thread offloads the work by firing off a task to do it using + method ``task_arena::enqueue`` of a ``task_arena`` instance. + When finished, the task posts an event to the GUI thread to indicate that the work is done. + The semantics of ``enqueue`` cause the task to eventually run on a worker thread + distinct from the calling thread. + + The following figure sketches the communication paths. Items in black are executed + by the GUI thread; items in blue are executed by another thread. + + |image0| + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + The example is for the Microsoft Windows\* operating systems, though + similar principles apply to any GUI using an event loop idiom. For + each event, the GUI thread calls a user-defined function ``WndProc`` to process an event. + + + :: + + + // Event posted from enqueued task when it finishes its work. + const UINT WM_POP_FOO = WM_USER+0; + + + // Queue for transmitting results from enqueued task to GUI thread. + oneapi::tbb::concurrent_queueResultQueue; + + + // GUI thread's private copy of most recently computed result. + Foo CurrentResult; +   + + LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDM_LONGRUNNINGWORK: + // User requested a long computation. Delegate it to another thread. + LaunchLongRunningWork(hWnd); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, msg, wParam, lParam); + } + break; + case WM_POP_FOO: + // There is another result in ResultQueue for me to grab. + ResultQueue.try_pop(CurrentResult); + // Update the window with the latest result. + RedrawWindow( hWnd, NULL, NULL, RDW_ERASE|RDW_INVALIDATE ); + break; + case WM_PAINT: + Repaint the window using CurrentResult + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc( hWnd, msg, wParam, lParam ); + } + return 0; + } + + + The GUI thread processes long computations as follows: + + + #. The GUI thread calls ``LongRunningWork``, which hands off the work + to a worker thread and immediately returns. + + + #. The GUI thread continues servicing the event loop. If it has to + repaint the window, it uses the value of\ ``CurrentResult``, which + is the most recent ``Foo`` that it has seen. + + + When a worker finishes the long computation, it pushes the result + into ResultQueue, and sends a message WM_POP_FOO to the GUI thread. + + + #. The GUI thread services a ``WM_POP_FOO`` message by popping an + item from ResultQueue into CurrentResult. The ``try_pop`` always + succeeds because there is exactly one ``WM_POP_FOO`` message for + each item in ``ResultQueue``. + + + Routine ``LaunchLongRunningWork`` creates a function task and launches it + using method ``task_arena::enqueue``. + + :: + + + class LongTask { + HWND hWnd; + void operator()() { + Do long computation + Foo x = result of long computation + ResultQueue.push( x ); + // Notify GUI thread that result is available. + PostMessage(hWnd,WM_POP_FOO,0,0); + } + public: + LongTask( HWND hWnd_ ) : hWnd(hWnd_) {} + }; + + void LaunchLongRunningWork( HWND hWnd ) { + oneapi::tbb::task_arena a; + a.enqueue(LongTask(hWnd)); + } + + + It is essential to use method ``task_arena::enqueue`` here. + Even though, an explicit ``task_arena`` instance is created, + the method ``enqueue`` ensures that the function task eventually executes when resources permit, + even if no thread explicitly waits on the task. In contrast, ``oneapi::tbb::task_group::run`` may + postpone execution of the function task until it is explicitly waited upon with the ``oneapi::tbb::task_group::wait``. + + The example uses a ``concurrent_queue`` for workers to communicate + results back to the GUI thread. Since only the most recent result + matters in the example, and alternative would be to use a shared + variable protected by a mutex. However, doing so would block the + worker while the GUI thread was holding a lock on the mutex, and vice + versa. Using ``concurrent_queue`` provides a simple robust solution. + + If two long computations are in flight, there is a chance that the + first computation completes after the second one. If displaying the + result of the most recently requested computation is important, then + associate a request serial number with the computation. The GUI + thread can pop from ``ResultQueue`` into a temporary variable, check + the serial number, and update ``CurrentResult`` only if doing so + advances the serial number. + +.. |image0| image:: Images/image007a.jpg + :width: 400px + :height: 150px diff --git a/_sources/main/tbb_userguide/design_patterns/General_References.rst b/_sources/main/tbb_userguide/design_patterns/General_References.rst new file mode 100644 index 0000000000..309d4aa602 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/General_References.rst @@ -0,0 +1,15 @@ +.. _General_References: + +General References +================== + + +This section lists general references. References specific to a pattern +are listed at the end of the topic for the pattern. + +- E. Gamma, R. Helm, R. Johnson, J. Vlissides. Design Patterns (1995) +- `Berkeley Pattern Language for Parallel Programming `_ +- T. Mattson, B. Sanders, B. Massingill. Patterns for Parallel Programming (2005) +- `ParaPLoP 2009 `_ +- `ParaPLoP 2010 `_ +- Eun-Gyu Kim and Marc Snir, `Parallel Programming Patterns `_ diff --git a/_sources/main/tbb_userguide/design_patterns/Lazy_Initialization.rst b/_sources/main/tbb_userguide/design_patterns/Lazy_Initialization.rst new file mode 100644 index 0000000000..6812e6c5e9 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Lazy_Initialization.rst @@ -0,0 +1,109 @@ +.. _Lazy_Initialization: + +Lazy Initialization +==================== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Delay the creation of an object, potentially expensive, until it is accessed. + In parallel programming, initialization must also be guarded against race conditions. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + The cost of operations that take place during the initialization + of the object may be considerably high. In that case, the object + should be initialized only when needed. Lazy initialization is + the common tactic that allows implementing such an approach. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Using ``oneapi::tbb::collaborative_call_once`` with ``oneapi::tbb::collaborative_once_flag`` + helps to implement thread-safe lazy initialization for a user object. + + + In addition, ``collaborative_call_once`` allows other thread blocked on + the same ``collaborative_once_flag`` to join other |short_name| + parallel constructions called within the initializing function. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + This example illustrates the implementation of lazy initialization + for the calculation of the Fibonacci numbers. Here is a graphical + representation of the Fibonacci recursion tree for N=4. + + + |image0| + + + As seen in the diagram, some elements are recalculated more than once. These operations are redundant, + so the "lazy initialized" Fibonacci numbers are relevant here. + + + An implementation without the use of lazy initialization would have *O(2^N)* time complexity due to + the full recursion tree traversal and recalculation of values. Since all the nodes are traversed once, + the tree becomes a list, making the time complexity *O(N)*. + + + |image1| + + + Here you can see the code for the implementation. Already calculated values are stored in a buffer + paired with ``collaborative_once_flag`` and will not be recalculated when ``collaborative_call_once`` + is invoked when initialization has already been done. + + + :: + + + using FibBuffer = std::vector>; + + std::uint64_t LazyFibHelper(int n, FibBuffer& buffer) { + // Base case + if (n <= 1) { + return n; + } + // Calculate nth value only once and store it in the buffer. + // Other threads won't be blocked on already taken collaborative_once_flag + // but join parallelism inside functor + oneapi::tbb::collaborative_call_once(buffer[n].first, [&]() { + std::uint64_t a, b; + oneapi::tbb::parallel_invoke([&] { a = LazyFibHelper(n - 2, buffer); }, + [&] { b = LazyFibHelper(n - 1, buffer); }); + buffer[n].second = a + b; + }); + + return buffer[n].second; + } + + std::uint64_t Fib(int n) { + FibBuffer buffer(n+1); + return LazyFibHelper(n, buffer); + } + + +.. |image0| image:: Images/image008a.jpg + :width: 744px + :height: 367px +.. |image1| image:: Images/image009a.jpg + :width: 744px + :height: 367px diff --git a/_sources/main/tbb_userguide/design_patterns/Local_Serializer.rst b/_sources/main/tbb_userguide/design_patterns/Local_Serializer.rst new file mode 100644 index 0000000000..d600bccad9 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Local_Serializer.rst @@ -0,0 +1,282 @@ +.. _Local_Serializer: + +Local Serializer +================ + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Consider an interactive program. To maximize concurrency and + responsiveness, operations requested by the user can be implemented + as tasks. The order of operations can be important. For example, + suppose the program presents editable text to the user. There might + be operations to select text and delete selected text. Reversing the + order of "select" and "delete" operations on the same buffer would be + bad. However, commuting operations on different buffers might be + okay. Hence the goal is to establish serial ordering of tasks + associated with a given object, but not constrain ordering of tasks + between different objects. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Operations associated with a certain object must be performed in + serial order. + + + - Serializing with a lock would be wasteful because threads would be + waiting at the lock when they could be doing useful work + elsewhere. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Sequence the work items using a FIFO (first-in first-out structure). + Always keep an item in flight if possible. If no item is in flight + when a work item appears, put the item in flight. Otherwise, push the + item onto the FIFO. When the current item in flight completes, pop + another item from the FIFO and put it in flight. + + + The logic can be implemented without mutexes, by using + ``concurrent_queue`` for the FIFO and ``atomic`` to count the + number of items waiting and in flight. The example explains the + accounting in detail. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + The following example builds on the Non-Preemptive Priorities example + to implement local serialization in addition to priorities. It + implements three priority levels and local serializers. The user + interface for it follows: + + + :: + + + enum Priority { + P_High, + P_Medium, + P_Low + }; +   + + template + void EnqueueWork( Priority p, Func f, Serializer* s=NULL ); + + + Template function ``EnqueueWork`` causes functor ``f`` to run when + the three constraints in the following table are met. + + + .. container:: tablenoborder + + + .. list-table:: + :header-rows: 1 + + * - Constraint + - Resolved by class... + * - Any prior work for the ``Serializer`` has completed. + - \ ``Serializer`` + * - A thread is available. + - \ ``RunWorkItem`` + * - No higher priority work is ready to run. + - \ ``ReadyPileType`` + + + + + Constraints on a given functor are resolved from top to bottom in the + table. The first constraint does not exist when s is NULL. The + implementation of ``EnqueueWork`` packages the functor in a + ``SerializedWorkItem`` and routes it to the class that enforces the + first relevant constraint between pieces of work. + + + :: + + + template + void EnqueueWork( Priority p, Func f, Serializer* s=NULL ) { + WorkItem* item = new SerializedWorkItem( p, f, s ); + if( s ) + s->add(item); + else + ReadyPile.add(item); + } + + + A ``SerializedWorkItem`` is derived from a ``WorkItem``, which serves + as a way to pass around a prioritized piece of work without knowing + further details of the work. + + + :: + + + // Abstract base class for a prioritized piece of work. + class WorkItem { + public: + WorkItem( Priority p ) : priority(p) {} + // Derived class defines the actual work. + virtual void run() = 0; + const Priority priority; + }; +   + + template + class SerializedWorkItem: public WorkItem { + Serializer* serializer; + Func f; + /*override*/ void run() { + f(); + Serializer* s = serializer; + // Destroy f before running Serializer’s next functor. + delete this; + if( s ) + s->noteCompletion(); + } + public: + SerializedWorkItem( Priority p, const Func& f_, Serializer* s ) : + WorkItem(p), serializer(s), f(f_) + {} + }; + + + Base class ``WorkItem`` is the same as class WorkItem in the example + for Non-Preemptive Priorities. The notion of serial constraints is + completely hidden from the base class, thus permitting the framework + to extend other kinds of constraints or lack of constraints. Class + ``SerializedWorkItem`` is essentially ``ConcreteWorkItem`` from the + example for Non-Preemptive Priorities, extended with a ``Serializer`` + aspect. + + + Virtual method ``run()`` is invoked when it becomes time to run the + functor. It performs three steps: + + + #. Run the functor. + + + #. Destroy the functor. + + + #. Notify the ``Serializer`` that the functor completed, and thus + unconstraining the next waiting functor. + + + Step 3 is the difference from the operation of ConcreteWorkItem::run. + Step 2 could be done after step 3 in some contexts to increase + concurrency slightly. However, the presented order is recommended + because if step 2 takes non-trivial time, it likely has side effects + that should complete before the next functor runs. + + + Class ``Serializer`` implements the core of the Local Serializer + pattern: + + + :: + + + class Serializer { + oneapi::tbb::concurrent_queue queue; + std::atomic count; // Count of queued items and in-flight item + void moveOneItemToReadyPile() { // Transfer item from queue to ReadyPile + WorkItem* item; + queue.try_pop(item); + ReadyPile.add(item); + } + public: + void add( WorkItem* item ) { + queue.push(item); + if( ++count==1 ) + moveOneItemToReadyPile(); + } + void noteCompletion() { // Called when WorkItem completes. + if( --count!=0 ) + moveOneItemToReadyPile(); + } + }; + + + The class maintains two members: + + + - A queue of WorkItem waiting for prior work to complete. + + + - A count of queued or in-flight work. + + + Mutexes are avoided by using ``concurrent_queue`` and + ``atomic`` along with careful ordering of operations. The + transitions of count are the key understanding how class + ``Serializer`` works. + + + - If method ``add`` increments ``count`` from 0 to 1, this indicates + that no other work is in flight and thus the work should be moved + to the ``ReadyPile``. + + + - If method ``noteCompletion`` decrements count and it is *not* from + 1 to 0, then the queue is non-empty and another item in the queue + should be moved to ``ReadyPile``. + + + Class ``ReadyPile`` is explained in the example for Non-Preemptive + Priorities. + + + If priorities are not necessary, there are two variations on method + ``moveOneItemToReadyPile``, with different implications. + + + - Method ``moveOneItemToReadyPile`` could directly + invoke\ ``item->run()``. This approach has relatively low overhead + and high thread locality for a given ``Serializer``. But it is + unfair. If the ``Serializer`` has a continual stream of tasks, the + thread operating on it will keep servicing those tasks to the + exclusion of others. + + + - Method ``moveOneItemToReadyPile`` could invoke ``task::enqueue`` + to enqueue a task that invokes ``item->run()``. Doing so + introduces higher overhead and less locality than the first + approach, but avoids starvation. + + + The conflict between fairness and maximum locality is fundamental. + The best resolution depends upon circumstance. + + + The pattern generalizes to constraints on work items more general + than those maintained by class Serializer. A generalized + ``Serializer::add`` determines if a work item is unconstrained, and + if so, runs it immediately. A generalized + ``Serializer::noteCompletion`` runs all previously constrained items + that have become unconstrained by the completion of the current work + item. The term "run" means to run work immediately, or if there are + more constraints, forwarding the work to the next constraint + resolver. + diff --git a/_sources/main/tbb_userguide/design_patterns/Non-Preemptive_Priorities.rst b/_sources/main/tbb_userguide/design_patterns/Non-Preemptive_Priorities.rst new file mode 100644 index 0000000000..4a9795e309 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Non-Preemptive_Priorities.rst @@ -0,0 +1,177 @@ +.. _Non-Preemptive_Priorities: + +Non-Preemptive Priorities +========================= + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Choose the next work item to do, based on priorities. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + The scheduler in |full_name| + chooses tasks using rules based on scalability concerns. The rules + are based on the order in which tasks were spawned or enqueued, and + are oblivious to the contents of tasks. However, sometimes it is best + to choose work based on some kind of priority relationship. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Given multiple work items, there is a rule for which item should + be done next that is *not* the default oneTBB rule. + + + - Preemptive priorities are not necessary. If a higher priority item + appears, it is not necessary to immediately stop lower priority + items in flight. If preemptive priorities are necessary, then + non-preemptive tasking is inappropriate. Use threads instead. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Put the work in a shared work pile. Decouple tasks from specific + work, so that task execution chooses the actual piece of work to be + selected from the pile. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + The following example implements three priority levels. The user + interface for it and top-level implementation follow: + + + :: + + + enum Priority { + P_High, + P_Medium, + P_Low + }; +   + + template + void EnqueueWork( Priority p, Func f ) { + WorkItem* item = new ConcreteWorkItem( p, f ); + ReadyPile.add(item); + } + + + The caller provides a priority ``p`` and a functor ``f`` to routine ``EnqueueWork``. + The functor may be the result of a lambda expression. ``EnqueueWork`` packages ``f`` as a ``WorkItem`` and adds + it to global object ``ReadyPile``. + + + Class ``WorkItem`` provides a uniform interface for running functors of unknown type: + + + :: + + + // Abstract base class for a prioritized piece of work. + class WorkItem { + public: + WorkItem( Priority p ) : priority(p) {} + // Derived class defines the actual work. + virtual void run() = 0; + const Priority priority; + }; +   + + template + class ConcreteWorkItem: public WorkItem { + Func f; + /*override*/ void run() { + f(); + delete this; + } + public: + ConcreteWorkItem( Priority p, const Func& f_ ) : + WorkItem(p), f(f_) + {} + }; + + + Class ``ReadyPile`` contains the core pattern. It maintains a + collection of work and fires off tasks through the ``oneapi::tbb::task_group::run`` interface + and then choose a work from the collection: + + + :: + + + class ReadyPileType { + // One queue for each priority level + oneapi::tbb::concurrent_queue level[P_Low+1]; + oneapi::tbb::task_group tg; + public: + void add( WorkItem* item ) { + level[item->priority].push(item); + tg.run(RunWorkItem()); + } + void runNextWorkItem() { + // Scan queues in priority order for an item. + WorkItem* item=NULL; + for( int i=P_High; i<=P_Low; ++i ) + if( level[i].try_pop(item) ) + break; + assert(item); + item->run(); + } + }; +   + + ReadyPileType ReadyPile; + + + The task added by ``add(item)`` does *not* necessarily execute + that item. The task itself executes ``runNextWorkItem()``, which may find a + higher priority item. There is one task for each item, but the + mapping resolves when the task actually executes, not when it is created. + + Here are the details of class ``RunWorkItem``: + + :: + + class RunWorkItem { + void operator()() { + ReadyPile.runNextWorkItem(); + }; + }; + + + ``RunWorkItem`` objects are fungible. They enable the oneTBB + scheduler to choose when to do a work item, not which work item to do. + + + Other priority schemes can be implemented by changing the internals + for ``ReadyPileType``. A priority queue could be used to implement + very fine grained priorities. + + The scalability of the pattern is limited by the scalability of + ``ReadyPileType``. Ideally scalable concurrent containers should be + used for it. + diff --git a/_sources/main/tbb_userguide/design_patterns/Odd-Even_Communication.rst b/_sources/main/tbb_userguide/design_patterns/Odd-Even_Communication.rst new file mode 100644 index 0000000000..749c1b57b1 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Odd-Even_Communication.rst @@ -0,0 +1,61 @@ +.. _Odd-Even_Communication: + +Odd-Even Communication +====================== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Operations on data cannot be done entirely independently, but data + can be partitioned into two subsets such that all operations on a + subset can run in parallel. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Solvers for partial differential equations can often be modified to + follow this pattern. For example, for a 2D grid with only + nearest-neighbor communication, it may be possible to treat the grid + as a checkerboard, and alternate between updating red squares and + black squares. + + + Another context is staggered grid ("leap frog") Finite Difference + Time Domain (FDTD solvers, which naturally fit the pattern. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Dependencies between items form a bipartite graph. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Alternate between updating one subset and then the other subset. + Apply the elementwise pattern to each subset. + +.. container:: section + + + .. rubric:: References + :class: sectiontitle + + Eun-Gyu Kim and Mark Snir, "Odd-Even Communication Group", + http://snir.cs.illinois.edu/patterns/oddeven.pdf + diff --git a/_sources/main/tbb_userguide/design_patterns/Reduction.rst b/_sources/main/tbb_userguide/design_patterns/Reduction.rst new file mode 100644 index 0000000000..491a95b9d4 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Reduction.rst @@ -0,0 +1,281 @@ +.. _Reduction: + +Reduction +========= + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Perform an associative reduction operation across a data set. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Many serial algorithms sweep over a set of items to collect summary + information. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + The summary can be expressed as an associative operation over the + data set, or at least is close enough to associative that + reassociation does not matter. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Two solutions exist in |full_name|. + The choice on which to use depends upon several considerations: + + + - Is the operation commutative as well as associative? + + + - Are instances of the reduction type expensive to construct and + destroy. For example, a floating point number is inexpensive to + construct. A sparse floating-point matrix might be very expensive + to construct. + + + Use ``oneapi::tbb::parallel_reduce`` when the objects are inexpensive to + construct. It works even if the reduction operation is not + commutative. + + + Use ``oneapi::tbb::parallel_for`` and ``oneapi::tbb::combinable`` if the reduction + operation is commutative and instances of the type are expensive. + + + If the operation is not precisely associative but a precisely + deterministic result is required, use recursive reduction and + parallelize it using ``oneapi::tbb::parallel_invoke``. + + +.. container:: section + + + .. rubric:: Examples + :class: sectiontitle + + The examples presented here illustrate the various solutions and some + tradeoffs. + + + The first example uses ``oneapi::tbb::parallel_reduce`` to do a + reduction + over sequence of type ``T``. The sequence is defined by a half-open + interval [first,last). + + + :: + + + T AssociativeReduce( const T* first, const T* last, T identity ) { + return oneapi::tbb::parallel_reduce( + // Index range for reduction + oneapi::tbb::blocked_range(first,last), + // Identity element + identity, + // Reduce a subrange and partial sum + [&]( oneapi::tbb::blocked_range r, T partial_sum )->float { + return std::accumulate( r.begin(), r.end(), partial_sum ); + }, + // Reduce two partial sums + std::plus() + ); + } + + + The third and fourth arguments to this form of ``parallel_reduce`` + are a built in form of the agglomeration pattern. If there is an + elementwise action to be performed before the reduction, + incorporating it into the third argument (reduction of a subrange) + may improve performance because of better locality of reference. Note + that the block size for agglomeration is not explicitly specified; + ``parallel_reduce`` defines blocks automatically with the help of + implicitly used ``oneapi::tbb::auto_partitioner``. + + + The second example assumes the + is commutative on ``T``. It is a + good solution when ``T`` objects are expensive to construct. + + + :: + + + T CombineReduce( const T* first, const T* last, T identity ) { + oneapi::tbb::combinable sum(identity); + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(first,last), + [&]( oneapi::tbb::blocked_range r ) { + sum.local() += std::accumulate(r.begin(), r.end(), identity); + } + ); + return sum.combine( []( const T& x, const T& y ) {return x+y;} ); + } + + + Sometimes it is desirable to destructively use the partial results to + generate the final result. For example, if the partial results are + lists, they can be spliced together to form the final result. In that + case use class ``oneapi::tbb::enumerable_thread_specific`` instead of + ``combinable``. The ``ParallelFindCollisions`` example in :ref:`Divide_and_Conquer` + demonstrates the technique. + + + Floating-point addition and multiplication are almost associative. + Reassociation can cause changes because of rounding effects. The + techniques shown so far reassociate terms non-deterministically. + Fully deterministic parallel reduction for a not quite associative + operation requires using deterministic reassociation. The code below + demonstrates this in the form of a template that does a + reduction + over a sequence of values of type ``T``. + + + :: + + + template + T RepeatableReduce( const T* first, const T* last, T identity ) { + if( last-first<=1000 ) { + // Use serial reduction + return std::accumulate( first, last, identity ); + } else { + // Do parallel divide-and-conquer reduction + const T* mid = first+(last-first)/2; + T left, right; + oneapi::tbb::parallel_invoke( + [&]{left=RepeatableReduce(first,mid,identity);}, + [&]{right=RepeatableReduce(mid,last,identity);} + ); + return left+right; + } + } + + + The outer if-else is an instance of the agglomeration pattern for + recursive computations. The reduction graph, though not a strict + binary tree, is fully deterministic. Thus the result will always be + the same for a given input sequence, assuming all threads do + identical floating-point rounding. + + + ``oneapi::tbb::parallel_deterministic_reduce`` is a simpler and more + efficient way to get reproducible non-associative reduction. It is + very similar to ``oneapi::tbb::parallel_reduce`` but, unlike the latter, + builds a deterministic reduction graph. With it, the + ``RepeatableReduce`` sample can be almost identical to + ``AssociativeReduce``: + + + :: + + + template + T RepeatableReduce( const T* first, const T* last, T identity ) { + return oneapi::tbb::parallel_deterministic_reduce( + // Index range for reduction + oneapi::tbb::blocked_range(first,last,1000), + // Identity element + identity, + // Reduce a subrange and partial sum + [&]( oneapi::tbb::blocked_range r, T partial_sum )->float { + return std::accumulate( r.begin(), r.end(), partial_sum ); + }, + // Reduce two partial sums + std::plus() + ); + } + + + Besides the function name change, note the grain size of 1000 + specified for ``oneapi::tbb::blocked_range``. It defines the desired block + size for agglomeration; automatic block size selection is not used + due to non-determinism. + + + The final example shows how a problem that typically is not viewed as + a reduction can be parallelized by viewing it as a reduction. The + problem is retrieving floating-point exception flags for a + computation across a data set. The serial code might look something + like: + + + :: + + + feclearexcept(FE_ALL_EXCEPT); + for( int i=0; i r ) { + int end=r.end(); + for( int i=r.begin(); i!=end; ++i ) + C[i] = A[i]/B[i]; + // It is critical to do |= here, not =, because otherwise we + // might lose earlier exceptions from the same thread. + flags |= fetestexcept(FE_ALL_EXCEPT); + } + // Called by parallel_reduce when joining results from two subranges. + void join( Body& other ) { + flags |= other.flags; + } + }; + + + Then invoke it as follows: + + + :: + + + // Construction of cc implicitly resets FP exception state. + ComputeChunk cc; + oneapi::tbb::parallel_reduce( oneapi::tbb::blocked_range(0,N), cc ); + if (cc.flags & FE_DIVBYZERO) ...; + if (cc.flags & FE_OVERFLOW) ...; + ... + diff --git a/_sources/main/tbb_userguide/design_patterns/Reference_Counting.rst b/_sources/main/tbb_userguide/design_patterns/Reference_Counting.rst new file mode 100644 index 0000000000..b82df15a30 --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Reference_Counting.rst @@ -0,0 +1,144 @@ +.. _Reference_Counting: + +Reference Counting +================== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Destroy an object when it will no longer be used. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + Often it is desirable to destroy an object when it is known that it + will not be used in the future. Reference counting is a common serial + solution that extends to parallel programming if done carefully. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - If there are cycles of references, basic reference counting is + insufficient unless the cycle is explicitly broken. + + + - Atomic counting is relatively expensive in hardware. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + Thread-safe reference counting is like serial reference counting, + except that the increment/decrement is done atomically, and the + decrement and test "count is zero?" must act as a single atomic + operation. The following example uses ``std::atomic`` to achieve + this. + + + :: + + + template + class counted { + std::atomic my_count; + T value; + public: + // Construct object with a single reference to it. + counted() {my_count=1;} + // Add reference + void add_ref() {++my_count;} + // Remove reference. Return true if it was the last reference. + bool remove_ref() {return --my_count==0;} + // Get reference to underlying object + T& get() { + assert(my_count>0); + return my_value; + } + }; + + + It is incorrect to use a separate read for testing if the count is + zero. The following code would be an incorrect implementation of + method ``remove_ref``\ () because two threads might both execute the + decrement, and then both read ``my_count`` as zero. Hence two callers + would both be told incorrectly that they had removed the last + reference. + + + :: + + + --my_count; + return my_count==0. // WRONG! + + + The decrement may need to have a *release* fence so that any pending + writes complete before the object is deleted. + + + There is no simple way to atomically copy a pointer and increment its + reference count, because there will be a timing hole between the + copying and the increment where the reference count is too low, and + thus another thread might decrement the count to zero and delete the + object. Two ways to address the problem are "hazard pointers" and + "pass the buck". See the references below for details. + + +.. container:: section + + + .. rubric:: Variations + :class: sectiontitle + + Atomic increment/decrement can be more than an order of magnitude + more expensive than ordinary increment/decrement. The serial + optimization of eliminating redundant increment/decrement operations + becomes more important with atomic reference counts. + + + Weighted reference counting can be used to reduce costs if the + pointers are unshared but the referent is shared. Associate a + *weight* with each pointer. The reference count is the sum of the + weights. A pointer ``x`` can be copied as a pointer ``x'`` without + updating the reference count by splitting the original weight between + ``x`` and ``x'``. If the weight of ``x`` is too low to split, then first add a + constant W to the reference count and weight of ``x``. + + +.. container:: section + + + .. rubric:: References + :class: sectiontitle + + D. Bacon and V.T. Rajan, "Concurrent Cycle Collection in Reference + Counted Systems" in Proc. European Conf. on Object-Oriented + Programming (June 2001). Describes a garbage collector based on + reference counting that does collect cycles. + + + M. Michael, "Hazard Pointers: Safe Memory Reclamation for Lock-Free + Objects" in IEEE Transactions on Parallel and Distributed Systems + (June 2004). Describes the "hazard pointer" technique. + + + M. Herlihy, V. Luchangco, and M. Moir, "The Repeat Offender Problem: + A Mechanism for Supporting Dynamic-Sized, Lock-Free Data Structures" + in Proceedings of the 16th International Symposium on Distributed + Computing (Oct. 2002). Describes the "pass the buck" technique. + diff --git a/_sources/main/tbb_userguide/design_patterns/Wavefront.rst b/_sources/main/tbb_userguide/design_patterns/Wavefront.rst new file mode 100644 index 0000000000..28776e4a8d --- /dev/null +++ b/_sources/main/tbb_userguide/design_patterns/Wavefront.rst @@ -0,0 +1,199 @@ +.. _Wavefront: + +Wavefront +========= + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Perform computations on items in a data set, where the computation on + an item uses results from computations on predecessor items. + + +.. container:: section + + + .. rubric:: Context + :class: sectiontitle + + The dependences between computations form an acyclic graph. + + +.. container:: section + + + .. rubric:: Forces + :class: sectiontitle + + - Dependence constraints between items form an acyclic graph. + + + - The number of immediate predecessors in the graph is known in + advance, or can be determined some time before the last + predecessor completes. + + +.. container:: section + + + .. rubric:: Solution + :class: sectiontitle + + The solution is a parallel variant of topological sorting, using + ``oneapi::tbb::parallel_for_each`` to process items. Associate an atomic + counter with each item. Initialize each counter to the number of + predecessors. Invoke ``oneapi::tbb::parallel_for_each`` to process the items that + have no predessors (have counts of zero). After an item is processed, + decrement the counters of its successors. If a successor's counter + reaches zero, add that successor to the ``oneapi::tbb::parallel_for_each`` + via a "feeder". + + + If the number of predecessors for an item cannot be determined in + advance, treat the information "know number of predecessors" as an + additional predecessor. When the number of predecessors becomes + known, treat this conceptual predecessor as completed. + + + If the overhead of counting individual items is excessive, aggregate + items into blocks, and do the wavefront over the blocks. + + +.. container:: section + + + .. rubric:: Example + :class: sectiontitle + + Below is a serial kernel for the longest common subsequence + algorithm. The parameters are strings ``x`` and ``y`` with respective + lengths ``xlen`` and ``ylen``. + + + :: + + + int F[MAX_LEN+1][MAX_LEN+1]; + + + void SerialLCS( const char* x, size_t xlen, const char* y, size_t ylen ) + { + for( size_t i=1; i<=xlen; ++i ) + for( size_t j=1; j<=ylen; ++j ) + F[i][j] = x[i-1]==y[j-1] ? F[i-1][j-1]+1: + max(F[i][j-1],F[i-1][j]); + } + + + The kernel sets ``F[i][j]`` to the length of the longest common + subsequence shared by ``x[0..i-1]`` and ``y[0..j-1]``. It assumes + that F[0][0..ylen] and ``F[0..xlen][0]`` have already been + initialized to zero. + + + The following figure shows the data dependences for calculating + ``F[i][j]``. + + + .. container:: fignone + :name: fig3 + + + Data dependences for longest common substring calculation. + |image0| + + + The following figure shows the gray diagonal dependence is the + transitive closure of other dependencies. Thus for parallelization + purposes it is a redundant dependence that can be ignored. + + + .. container:: fignone + :name: fig4 + + + Diagonal dependence is redundant. + |image1| + + + It is generally good to remove redundant dependences from + consideration, because the atomic counting incurs a cost for each + dependence considered. + + + Another consideration is grain size. Scheduling each ``F[i][j]`` + element calculation separately is prohibitively expensive. A good + solution is to aggregate the elements into contiguous blocks, and + process the contents of a block serially. The blocks have the same + dependence pattern, but at a block scale. Hence scheduling overheads + can be amortized over blocks. + + + The parallel code follows. Each block consists of ``N×N`` elements. + Each block has an associated atomic counter. Array ``Count`` + organizes these counters for easy lookup. The code initializes the + counters and then rolls a wavefront using ``parallel_for_each``, + starting with the block at the origin since it has no predecessors. + + + :: + + + const int N = 64; + std::atomic Count[MAX_LEN/N+1][MAX_LEN/N+1]; +   + + void ParallelLCS( const char* x, size_t xlen, const char* y, size_t ylen ) { + // Initialize predecessor counts for blocks. + size_t m = (xlen+N-1)/N; + size_t n = (ylen+N-1)/N; + for( int i=0; i0)+(j>0); + // Roll the wavefront from the origin. + typedef pair block; + block origin(0,0); + oneapi::tbb::parallel_for_each( &origin, &origin+1, + [=]( const block& b, oneapi::tbb::feeder&feeder ) { + // Extract bounds on block + size_t bi = b.first; + size_t bj = b.second; + size_t xl = N*bi+1; + size_t xu = min(xl+N,xlen+1); + size_t yl = N*bj+1; + size_t yu = min(yl+N,ylen+1); + // Process the block + for( size_t i=xl; i f( g, 1, []( int i ) -> int { + return spin_for(i); + } ); + f.try_put(1); + g.wait_for_all(); + } + }; + + + void no_wait_for_all_enqueue() { + task_arena a; + a.enqueue(background_task()); + // do other things without waiting… + } + + +In the code snippet above, the enqueued task executes at some point, but +it's not clear when. If you need to use the results of the enqueued +task, or even ensure that it completes before the program ends, you will +need to use some mechanism to signal from the enqueued task that the +graph is complete. + diff --git a/_sources/main/tbb_userguide/estimate_flow_graph_performance.rst b/_sources/main/tbb_userguide/estimate_flow_graph_performance.rst new file mode 100644 index 0000000000..e5af0c2468 --- /dev/null +++ b/_sources/main/tbb_userguide/estimate_flow_graph_performance.rst @@ -0,0 +1,52 @@ +.. _estimate_flow_graph_performance: + +Estimating Flow Graph Performance +================================= + + +The performance or scalability of a flow graph is not easy to predict. +However there are a few key points that can guide you in estimating the +limits on performance and speedup of some graphs. + + +.. container:: section + + + .. rubric:: The Critical Path Limits the Scalability in a Dependence + Graph + :class: sectiontitle + + A critical path is the most time consuming path from a node with no + predecessors to a node with no successors. In a dependence graph, the + execution of the nodes along a path cannot be overlapped since they + have a strict ordering. Therefore, for a dependence graph, the + critical path limits scalability. + + + More formally, let T be the total time consumed by all of the nodes + in your graph if executed sequentially. Then let C be the time + consumed along the path that takes the most time. The nodes along + this path cannot be overlapped even in a parallel execution. + Therefore, even if all other paths are executed in parallel with C, + the wall clock time for the parallel execution is at least C, and the + maximum possible speedup (ignoring microarchitectural and memory + effects) is T/C. + + +.. container:: section + + + .. rubric:: There is Overhead in Spawning a Node's Body as a Task + :class: sectiontitle + + The bodies of ``input_nodes``, ``function_nodes``, ``continue_nodes`` and + ``multifunction_nodes`` execute within spawned tasks by default. This + means that you need to take into account the overhead of task + scheduling when estimating the time it takes for a node to execute + its body. All of the rules of thumb for determining the appropriate + granularity of tasks therefore also apply to node bodies as well. If + you have many fine-grained nodes in your flow graph, the impact of + these overheads can noticeably impact your performance. However, + depending on the graph structure, you can reduce such overheads by + using lightweight policy with these nodes. + diff --git a/_sources/main/tbb_userguide/parallel_for_os.rst b/_sources/main/tbb_userguide/parallel_for_os.rst new file mode 100644 index 0000000000..cbc7578f4c --- /dev/null +++ b/_sources/main/tbb_userguide/parallel_for_os.rst @@ -0,0 +1,125 @@ +.. _parallel_for: + +parallel_for +============ + + +Suppose you want to apply a function ``Foo`` to each element of an +array, and it is safe to process each element concurrently. Here is the +sequential code to do this: + + +:: + + + void SerialApplyFoo( float a[], size_t n ) { + for( size_t i=0; i!=n; ++i ) + Foo(a[i]); + } + + +The iteration space here is of type ``size_t``, and goes from ``0`` to +``n-1``. The template function ``oneapi::tbb::parallel_for`` breaks this iteration +space into chunks, and runs each chunk on a separate thread. The first +step in parallelizing this loop is to convert the loop body into a form +that operates on a chunk. The form is an STL-style function object, +called the *body* object, in which ``operator()`` processes a chunk. The +following code declares the body object. + +:: + + #include "oneapi/tbb.h" + + using namespace oneapi::tbb; + + class ApplyFoo { + float *const my_a; + public: + void operator()( const blocked_range& r ) const { + float *a = my_a; + for( size_t i=r.begin(); i!=r.end(); ++i ) + Foo(a[i]); + } + ApplyFoo( float a[] ) : + my_a(a) + {} + }; + + +The ``using`` directive in the example enables you to use the library +identifiers without having to write out the namespace prefix ``oneapi::tbb`` +before each identifier. The rest of the examples assume that such a +``using`` directive is present. + + +Note the argument to ``operator()``. A ``blocked_range`` is a +template class provided by the library. It describes a one-dimensional +iteration space over type ``T``. Class ``parallel_for`` works with other +kinds of iteration spaces too. The library provides ``blocked_range2d``, +``blocked_range3d``, and ``blocked_nd_range`` for multidimensional spaces. +You can define your own spaces as explained +in :ref:`Advanced_Topic_Other_Kinds_of_Iteration_Spaces`. + + +An instance of ``ApplyFoo`` needs member fields that remember all the +local variables that were defined outside the original loop but used +inside it. Usually, the constructor for the body object will initialize +these fields, though ``parallel_for`` does not care how the body object +is created. Template function ``parallel_for`` requires that the body +object have a copy constructor, which is invoked to create a separate +copy (or copies) for each worker thread. It also invokes the destructor +to destroy these copies. In most cases, the implicitly generated copy +constructor and destructor work correctly. If they do not, it is almost +always the case (as usual in C++) that you must define *both* to be +consistent. + + +Because the body object might be copied, its ``operator()`` should not +modify the body. Otherwise the modification might or might not become +visible to the thread that invoked ``parallel_for``, depending upon +whether ``operator()`` is acting on the original or a copy. As a +reminder of this nuance, ``parallel_for`` requires that the body +object's ``operator()`` be declared ``const``. + + +The example ``operator()`` loads ``my_a`` into a local variable ``a``. +Though not necessary, there are two reasons for doing this in the +example: + + +- **Style**. It makes the loop body look more like the original. + + +- **Performance**. Sometimes putting frequently accessed values into + local variables helps the compiler optimize the loop better, because + local variables are often easier for the compiler to track. + + +Once you have the loop body written as a body object, invoke the +template function ``parallel_for``, as follows: + + +:: + + + #include "oneapi/tbb.h" +   + + void ParallelApplyFoo( float a[], size_t n ) { + parallel_for(blocked_range(0,n), ApplyFoo(a)); + } + + +The ``blocked_range`` constructed here represents the entire iteration +space from 0 to n-1, which ``parallel_for`` divides into subspaces for +each processor. The general form of the constructor is +``blocked_range(begin,end,grainsize)``. The ``T`` specifies the value +type. The arguments ``begin`` and ``end`` specify the iteration space +STL-style as a half-open interval [``begin``,\ ``end``). The argument +*grainsize* is explained in the :ref:`Controlling_Chunking` section. The +example uses the default grainsize of 1 because by default +``parallel_for`` applies a heuristic that works well with the default +grainsize. + +.. include:: parallel_for_toctree.rst + diff --git a/_sources/main/tbb_userguide/parallel_for_toctree.rst b/_sources/main/tbb_userguide/parallel_for_toctree.rst new file mode 100644 index 0000000000..75e8fb5c6a --- /dev/null +++ b/_sources/main/tbb_userguide/parallel_for_toctree.rst @@ -0,0 +1,10 @@ +.. _parallel_for_toctree: + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Lambda_Expressions + ../tbb_userguide/Automatic_Chunking + ../tbb_userguide/Controlling_Chunking_os + ../tbb_userguide/Bandwidth_and_Cache_Affinity_os + ../tbb_userguide/Partitioner_Summary \ No newline at end of file diff --git a/_sources/main/tbb_userguide/parallel_reduce.rst b/_sources/main/tbb_userguide/parallel_reduce.rst new file mode 100644 index 0000000000..328decb8ee --- /dev/null +++ b/_sources/main/tbb_userguide/parallel_reduce.rst @@ -0,0 +1,186 @@ +.. _parallel_reduce: + +parallel_reduce +=============== + + +A loop can do a reduction, as in this summation: + + +:: + + + float SerialSumFoo( float a[], size_t n ) { + float sum = 0; + for( size_t i=0; i!=n; ++i ) + sum += Foo(a[i]); + return sum; + } + + +If the iterations are independent, you can parallelize this loop using +the template class ``parallel_reduce`` as follows: + + +:: + + + float ParallelSumFoo( const float a[], size_t n ) { + SumFoo sf(a); + parallel_reduce( blocked_range(0,n), sf ); + return sf.my_sum; + } + + +The class ``SumFoo`` specifies details of the reduction, such as how to +accumulate subsums and combine them. Here is the definition of class +``SumFoo``: + + +:: + + + class SumFoo { + float* my_a; + public: + float my_sum; + void operator()( const blocked_range& r ) { + float *a = my_a; + float sum = my_sum; + size_t end = r.end(); + for( size_t i=r.begin(); i!=end; ++i ) + sum += Foo(a[i]); + my_sum = sum; + } +   + + SumFoo( SumFoo& x, split ) : my_a(x.my_a), my_sum(0) {} +   + + void join( const SumFoo& y ) {my_sum+=y.my_sum;} + + + SumFoo(float a[] ) : + my_a(a), my_sum(0) + {} + }; + + +Note the differences with class ``ApplyFoo`` from parallel_for. First, +``operator()`` is *not* ``const``. This is because it must update +SumFoo::my_sum. Second, ``SumFoo`` has a *splitting constructor* and a +method ``join`` that must be present for ``parallel_reduce`` to work. +The splitting constructor takes as arguments a reference to the original +object, and a dummy argument of type ``split``, which is defined by the +library. The dummy argument distinguishes the splitting constructor from +a copy constructor. + + +.. tip:: + In the example, the definition of ``operator()`` uses local temporary + variables (``a``, ``sum``, ``end``) for scalar values accessed inside + the loop. This technique can improve performance by making it obvious + to the compiler that the values can be held in registers instead of + memory. If the values are too large to fit in registers, or have + their address taken in a way the compiler cannot track, the technique + might not help. With a typical optimizing compiler, using local + temporaries for only written variables (such as ``sum`` in the + example) can suffice, because then the compiler can deduce that the + loop does not write to any of the other locations, and hoist the + other reads to outside the loop. + + +When a worker thread is available, as decided by the task scheduler, +``parallel_reduce`` invokes the splitting constructor to create a +subtask for the worker. When the subtask completes, ``parallel_reduce`` +uses method ``join`` to accumulate the result of the subtask. The graph +at the top of the following figure shows the split-join sequence that +happens when a worker is available: + + +.. container:: fignone + :name: fig5 + + + Graph of the Split-join Sequence + |image0| + + +An arrows in the above figure indicate order in time. The splitting +constructor might run concurrently while object ``x`` is being used for the +first half of the reduction. Therefore, all actions of the splitting +constructor that creates y must be made thread safe with respect to ``x``. +So if the splitting constructor needs to increment a reference count +shared with other objects, it should use an atomic increment. + + +If a worker is not available, the second half of the iteration is +reduced using the same body object that reduced the first half. That is +the reduction of the second half starts where reduction of the first +half finished. + + +.. CAUTION:: + Since split/join are not used if workers are unavailable, + ``parallel_reduce`` does not necessarily do recursive splitting. + + +.. CAUTION:: + Since the same body might be used to accumulate multiple subranges, + it is critical that ``operator()`` not discard earlier accumulations. + The code below shows an incorrect definition of + ``SumFoo::operator()``. + + +:: + + + class SumFoo { + ... + public: + float my_sum; + void operator()( const blocked_range& r ) { + ... + float sum = 0; // WRONG – should be 'sum = my_sum". + ... + for( ... ) + sum += Foo(a[i]); + my_sum = sum; + } + ... + }; + + +With the mistake, the body returns a partial sum for the last subrange +instead of all subranges to which ``parallel_reduce`` applies it. + + +The rules for partitioners and grain sizes for ``parallel_reduce`` are +the same as for ``parallel_for``. + + +``parallel_reduce`` generalizes to any associative operation. In +general, the splitting constructor does two things: + + +- Copy read-only information necessary to run the loop body. + + +- Initialize the reduction variable(s) to the identity element of the + operation(s). + + +The join method should do the corresponding merge(s). You can do more +than one reduction at the same time: you can gather the min and max with +a single ``parallel_reduce``. + + +.. note:: + The reduction operation can be non-commutative. The example still + works if floating-point addition is replaced by string concatenation. + + +.. |image0| image:: Images/image009.jpg + :width: 512px + :height: 438px + diff --git a/_sources/main/tbb_userguide/std_invoke.rst b/_sources/main/tbb_userguide/std_invoke.rst new file mode 100644 index 0000000000..d536eae8b2 --- /dev/null +++ b/_sources/main/tbb_userguide/std_invoke.rst @@ -0,0 +1,217 @@ +.. _std_invoke: + +Invoke a Callable Object +========================== + +Starting from C++17, the requirements for callable objects passed to algorithms or Flow Graph nodes are relaxed. It allows using additional types of bodies. +Previously, the body of the algorithm or Flow Graph node needed to be a Function Object (see `C++ Standard Function Object `_) and provide an +``operator()`` that accepts input parameters. + +Now the body needs to meet the more relaxed requirements of being Callable (see `C++ Standard Callable `_) that covers three types of objects: + +* **Function Objects that provide operator(arg1, arg2, ...)**, which accepts the input parameters +* **Pointers to member functions** that you can use as the body of the algorithm or the Flow Graph node +* **Pointers to member objects** work as the body of the algorithm or parallel construct + +You can use it not only for a Flow Graph but also for algorithms. See the example below: + +.. code:: + + // The class models oneTBB Range + class StrideRange { + public: + StrideRange(int* s, std::size_t sz, std::size_t str) + : start(s), size(sz), stride(str) {} + + // A copy constructor + StrideRange(const StrideRange&) = default; + + // A splitting constructor + StrideRange(StrideRange& other, oneapi::tbb::split) + : start(other.start), size(other.size / 2) + { + other.size -= size; + other.start += size; + } + + ~StrideRange() = default; + + // Indicate if the range is empty + bool empty() const { + return size == 0; + } + + // Indicate if the range can be divided + bool is_divisible() const { + return size >= stride; + } + + void iterate() const { + for (std::size_t i = 0; i < size; i += stride) { + // Performed an action for each element of the range, + // implement the code based on your requirements + } + } + + private: + int* start; + std::size_t size; + std::size_t stride; + }; + +Where: + +* The ``StrideRange`` class models oneTBB range that should be iterated with a specified stride during its initial construction. +* The ``stride`` value is stored in a private field within the range. Therefore, the class provides the member function ``iterate() const`` that implements a loop with the specified stride. + +``range.iterate()`` +******************* + +Before C++17, to utilize a range in a parallel algorithm, such as ``parallel_for``, it was required to provide a ``Function Object`` as the algorithm's body. This Function Object defined the operations to be executed on each iteration of the range: + +.. code:: + + int main() { + std::size_t array_size = 1000; + + int* array_to_iterate = new int[array_size]; + + StrideRange range(array_to_iterate, array_size, /* stride = */ 2); + + // Define a lambda function as the body of the parallel_for loop + auto pfor_body = [] (const StrideRange& range) { + range.iterate(); + }; + + // Perform parallel iteration + oneapi::tbb::parallel_for(range, pfor_body); + + delete[] array_to_iterate; + } + +An additional lambda function ``pfor_body`` was also required. This lambda function invoked the ``rage.iterate()`` function. + +Now with C++17, you can directly utilize a pointer to ``range.iterate()`` as the body of the algorithm: + +.. code:: + + int main() { + std::size_t array_size = 1000; + + int* array_to_iterate = new int[array_size]; + + // Performs the iteration over the array elements with the specified stride + StrideRange range(array_to_iterate, array_size, /* stride = */ 2); + + // Parallelize the iteration over the range object + oneapi::tbb::parallel_for(range, &StrideRange::iterate); + + delete[] array_to_iterate; + } + +``std::invoke`` +**************** + +``std::invoke`` is a function template that provides a syntax for invoking different types of callable objects with a set of arguments. + +oneTBB implementation uses the C++ standard function ``std::invoke(&StrideRange::iterate, range)`` to execute the body. It is the equivalent of ``range.iterate()``. +Therefore, it allows you to invoke a callable object, such as a function object, with the provided arguments. + +.. tip:: Refer to `C++ Standard `_ to learn more about ``std::invoke``. + +Example +^^^^^^^^ + +Consider a specific scenario with ``function_node`` within a Flow Graph. + +In the example below, a ``function_node`` takes an object as an input to read a member object of that input and proceed it to the next node in the graph: + +.. code:: + + struct Object { + int number; + }; + + int main() { + using namespace oneapi::tbb::flow; + + // Lambda function to read the member object of the input Object + auto number_reader = [] (const Object& obj) { + return obj.number; + }; + + // Lambda function to process the received integer + auto number_processor = [] (int i) { /* processing integer */ }; + + graph g; + + // Function node that takes an Object as input and produces an integer + function_node func1(g, unlimited, number_reader); + + // Function node that takes an integer as input and processes it + function_node func2(g, unlimited, number_processor); + + // Connect the function nodes + make_edge(func1, func2); + + // Provide produced input to the graph + func1.try_put(Object{1}); + + // Wait for the graph to complete + g.wait_for_all(); + } + + +Before C++17, the ``function_node`` in the Flow Graph required the body to be a Function Object. A lambda function was required to extract the number from the Object. + +With C++17, you can use ``std::invoke`` with a pointer to the member number directly as the body. + +You can update the previous example as follows: + +.. code:: + + struct Object { + int number; + }; + + int main() { + using namespace oneapi::tbb::flow; + + // The processing logic for the received integer + auto number_processor = [] (int i) { /* processing integer */ }; + + // Create a graph object g to hold the flow graph + graph g; + + // Use a member function pointer to the number member of the Object struct as the body + function_node func1(g, unlimited, &Object::number); + + // Use the number_processor lambda function as the body + function_node func2(g, unlimited, number_processor); + + // Connect the function nodes + make_edge(func1, func2); + + // Connect the function nodes + func1.try_put(Object{1}); + + // Wait for the graph to complete + g.wait_for_all(); + } + +Find More +********* + +The following APIs supports Callable object as Bodies: + +* `parallel_for `_ +* `parallel_reduce `_ +* `parallel_deterministic_reduce `_ +* `parallel_for_each `_ +* `parallel_scan `_ +* `parallel_pipeline `_ +* `function_node `_ +* `multifunction_node `_ +* `async_node `_ +* `sequencer_node `_ +* `join_node with key_matching policy `_ diff --git a/_sources/main/tbb_userguide/title.rst b/_sources/main/tbb_userguide/title.rst new file mode 100644 index 0000000000..8adb7093fe --- /dev/null +++ b/_sources/main/tbb_userguide/title.rst @@ -0,0 +1,29 @@ +.. _title: + +|short_name| Developer Guide +============================ + +|full_name| + +.. toctree:: + :maxdepth: 4 + + ../tbb_userguide/Package_Contents_os + ../tbb_userguide/Parallelizing_Simple_Loops_os + ../tbb_userguide/Parallelizing_Complex_Loops + ../tbb_userguide/Flow_Graph + ../tbb_userguide/work_isolation + ../tbb_userguide/Exceptions_and_Cancellation + ../tbb_userguide/Floating_Point_Settings + ../tbb_userguide/Containers + ../tbb_userguide/Mutual_Exclusion + ../tbb_userguide/Timing + ../tbb_userguide/Memory_Allocation + ../tbb_userguide/The_Task_Scheduler + ../tbb_userguide/design_patterns/Design_Patterns + ../tbb_userguide/Migration_Guide + ../tbb_userguide/Constraints + ../tbb_userguide/std_invoke + ../tbb_userguide/appendix_A + ../tbb_userguide/appendix_B + ../tbb_userguide/References diff --git a/_sources/main/tbb_userguide/use_concurrency_limits.rst b/_sources/main/tbb_userguide/use_concurrency_limits.rst new file mode 100644 index 0000000000..2834372b76 --- /dev/null +++ b/_sources/main/tbb_userguide/use_concurrency_limits.rst @@ -0,0 +1,79 @@ +.. _use_concurrency_limits: + +Use Concurrency Limits +====================== + + +To control the number of instances of a single node, you can use the +concurrency limit on the node. To cause it to reject messages after it +reaches its concurrency limit, you construct it as a "rejecting" node. + + +A function node is constructed with one or more template arguments. The +third argument controls the buffer policy used by the node, and is by +default queueing. With a queueing policy, a ``function_node`` that has +reached its concurrency limit still accepts incoming messages, but +buffers them internally. If the policy is set to rejecting the node will +instead reject the incoming messages. + + +:: + + + template < typename Input, + typename Output = continue_msg, + graph_buffer_policy = queueing > + class function_node; + + +For example, you can control the number of big objects in flight in a +graph by placing a rejecting function_node downstream of an ``input_node``, +as is done below: + + +:: + + + graph g; + + + int src_count = 0; + int number_of_objects = 0; + int max_objects = 3; + + + input_node< big_object * > s( g, [&]( oneapi::tbb::flow_control& fc ) -> big_object* { + if ( src_count < M ) { + big_object* v = new big_object(); + ++src_count; + return v; + } else { + fc.stop(); + return nullptr; + } + } ); + s.activate(); + + function_node< big_object *, continue_msg, rejecting > f( g, 3, + []( big_object *v ) -> continue_msg { + spin_for(1); + delete v; + return continue_msg(); + } ); + + + make_edge( s, f ); + g.wait_for_all(); + + +The ``function_node`` will operate on at most three big objects +concurrently. The node's concurrency threshold that limits the node to +three concurrent invocations. When the ``function_node`` is running three +instances concurrently, it will start rejecting incoming messages from +the ``input_node``, causing the ``input_node`` to buffer its last created +object and temporarily stop invoking its body object. Whenever the +``function_node`` drops below its concurrency limit, it will pull new +messages from the ``input_node``. At most four big objects will exist +simultaneously, three in the ``function_node`` and one buffered in the +``input_node``. + diff --git a/_sources/main/tbb_userguide/use_graph_reset.rst b/_sources/main/tbb_userguide/use_graph_reset.rst new file mode 100644 index 0000000000..2fbcf28652 --- /dev/null +++ b/_sources/main/tbb_userguide/use_graph_reset.rst @@ -0,0 +1,30 @@ +.. _use_graph_reset: + +Use graph::reset() to Reset a Canceled Graph +============================================ + + +When a graph execution is canceled either because of an unhandled +exception or because its task_group_context is canceled explicitly, the +graph and its nodes may be left in an indeterminate state. For example, +in the code samples shown in :ref:`cancel_a_graph` the input 2 may be +left in a buffer. But even beyond remnants in the buffers, there are +other optimizations performed during the execution of a flow graph that +can leave its nodes and edges in an indeterminate state. If you want to +re-execute or restart a graph, you first need to reset the graph: + + +:: + + + try { + g.wait_for_all(); + } catch ( int j ) { + cout << "Caught " << j << "\n"; + // do something to fix the problem + g.reset(); + f1.try_put(1); + f1.try_put(2); + g.wait_for_all(); + } + diff --git a/_sources/main/tbb_userguide/use_input_node.rst b/_sources/main/tbb_userguide/use_input_node.rst new file mode 100644 index 0000000000..79e4952f14 --- /dev/null +++ b/_sources/main/tbb_userguide/use_input_node.rst @@ -0,0 +1,112 @@ +.. _use_input_node: + +Using input_node +================= + + +By default, an ``input_node`` is constructed in the inactive state: + + +:: + + + template< typename Body > input_node( graph &g, Body body, bool is_active=true ) + + +To activate an inactive ``input_node``, you call the node's function +activate: + + +:: + + + input_node< int > src( g, src_body(10), false ); + // use it in calls to make_edge… + src.activate(); + + +All ``input_node`` objects are constructed in the inactive state and usually +activated after the entire flow graph is constructed. + + +For example, you can use the code in :ref:`Data_Flow_Graph`. In that implementation, +the ``input_node`` is constructed in the inactive state and activated after +all other edges are made: + + +:: + + + make_edge( squarer, summer ); + make_edge( cuber, summer ); + input_node< int > src( g, src_body(10), false ); + make_edge( src, squarer ); + make_edge( src, cuber ); + src.activate(); + g.wait_for_all(); + + +In this example, if the ``input_node`` was toggled to the active state at the beginning, +it might send a message to squarer immediately after the edge to +squarer is connected. Later, when the edge to cuber is connected, cuber +will receive all future messages, but may have already missed some. + + +In general it is safest to create your ``input_node`` objects in the inactive +state and then activate them after the whole graph is constructed. +However, this approach serializes graph construction and graph +execution. + + +Some graphs can be constructed safely with ``input_node``s active, allowing +the overlap of construction and execution. If your graph is a directed +acyclic graph (DAG), and each ``input_node`` has only one successor, you +can activate your ``input_node``s just after their construction if you construct the +edges in reverse topological order; that is, make the edges at the +largest depth in the tree first, and work back to the shallowest edges. +For example, if src is an ``input_node`` and ``func1`` and ``func2`` are both +function nodes, the following graph would not drop messages, even though +src is activated just after its construction: + + +:: + + + const int limit = 10; + int count = 0; + graph g; + oneapi::tbb::flow::graph g; + oneapi::tbb::flow::input_node src( g, [&]( oneapi::tbb::flow_control &fc ) -> int { + if ( count < limit ) { + return ++count; + } + fc.stop(); + return {}; + }); + src.activate(); + + oneapi::tbb::flow::function_node func1( g, 1, []( int i ) -> int { + std::cout << i << "\n"; + return i; + } ); + oneapi::tbb::flow::function_node func2( g, 1, []( int i ) -> int { + std::cout << i << "\n"; + return i; + } ); + + + make_edge( func1, func2 ); + make_edge( src, func1 ); + + + g.wait_for_all(); + + +The above code is safe because the edge from ``func1`` to ``func2`` is made +before the edge from src to ``func1``. If the edge from src to func1 were +made first, ``func1`` might generate a message before ``func2`` is attached to +it; that message would be dropped. Also, src has only a single +successor. If src had more than one successor, the successor that is +attached first might receive messages that do not reach the successors +that are attached after it. + diff --git a/_sources/main/tbb_userguide/use_limiter_node.rst b/_sources/main/tbb_userguide/use_limiter_node.rst new file mode 100644 index 0000000000..6414b2ffd5 --- /dev/null +++ b/_sources/main/tbb_userguide/use_limiter_node.rst @@ -0,0 +1,87 @@ +.. _use_limiter_node: + +Using limiter_node +================== + + +One way to limit resource consumption is to use a limiter_node to set a +limit on the number of messages that can flow through a given point in +your graph. The constructor for a limiter node takes two arguments: + + +:: + + + limiter_node( graph &g, size_t threshold ) + + +The first argument is a reference to the graph it belongs to. The second +argument sets the maximum number of items that should be allowed to pass +through before the node starts rejecting incoming messages. + + +A limiter_node maintains an internal count of the messages that it has +allowed to pass. When a message leaves the controlled part of the graph, +a message can be sent to the decrement port on the ``limiter_node`` to +decrement the count, allowing additional messages to pass through. In +the example below, an ``input_node`` will generate ``M`` big objects. But the +user wants to allow at most three big objects to reach the ``function_node`` +at a time, and to prevent the ``input_node`` from generating all ``M`` big +objects at once. + + +:: + + + graph g; + + + int src_count = 0; + int number_of_objects = 0; + int max_objects = 3; + + + input_node< big_object * > s( g, [&]( oneapi::tbb::flow_control& fc ) -> big_object* { + if ( src_count < M ) { + big_object* v = new big_object(); + ++src_count; + return v; + } else { + fc.stop(); + return nullptr; + } + } ); + s.activate(); + + limiter_node< big_object * > l( g, max_objects ); + + + function_node< big_object *, continue_msg > f( g, unlimited, + []( big_object *v ) -> continue_msg { + spin_for(1); + delete v; + return continue_msg(); + } ); + + + + + make_edge( l, f ); + make_edge( f, l.decrement ); + make_edge( s, l ); + g.wait_for_all(); + + +The example above prevents the ``input_node`` from generating all ``M`` big +objects at once. The ``limiter_node`` has a threshold of 3, and will +therefore start rejecting incoming messages after its internal count +reaches 3. When the ``input_node`` sees its message rejected, it stops +calling its body object and temporarily buffers the last generated +value. The ``function_node`` has its output, a ``continue_msg``, sent to the +decrement port of the ``limiter_node``. So, after it completes executing, +the ``limiter_node`` internal count is decremented. When the internal count +drops below the threshold, messages begin flowing from the ``input_node`` +again. So in this example, at most four big objects exist at a time, the +three that have passed through the ``limiter_node`` and the one that is +buffered in the ``input_node``. + diff --git a/_sources/main/tbb_userguide/use_make_edge.rst b/_sources/main/tbb_userguide/use_make_edge.rst new file mode 100644 index 0000000000..01b97609e4 --- /dev/null +++ b/_sources/main/tbb_userguide/use_make_edge.rst @@ -0,0 +1,25 @@ +.. _use_make_edge: + +Use make_edge and remove_edge +============================= + + +These are the basic guidelines for creating and removing edges: + + +- use make_edge and remove_edge + + +- Avoid using register_successor and register_predecessor + + +- Avoid using remove_successor and remove_predecessor + + +As a convention, to communicate the topology, use only functions +flow::make_edge and flow::remove_edge. The runtime library uses node +functions, such as sender::register_successor, to create these edges, +but those functions should not be called directly. The runtime library +calls these node functions directly to implement optimizations on the +topology at runtime. + diff --git a/_sources/main/tbb_userguide/use_nested_algorithms.rst b/_sources/main/tbb_userguide/use_nested_algorithms.rst new file mode 100644 index 0000000000..a20cd55f43 --- /dev/null +++ b/_sources/main/tbb_userguide/use_nested_algorithms.rst @@ -0,0 +1,68 @@ +.. _use_nested_algorithms: + +Use Nested Algorithms to Increase Scalability +============================================= + + +One powerful way to increase the scalability of a flow graph is to nest +other parallel algorithms inside of node bodies. Doing so, you can use a +flow graph as a coordination language, expressing the most +coarse-grained parallelism at the level of the graph, with finer grained +parallelism nested within. + + +In the example below, five nodes are created: an ``input_node``, +``matrix_source``, that reads a sequence of matrices from a file, two +``function_nodes``, ``n1`` and ``n2``, that receive these matrices and generate two +new matrices by applying a function to each element, and two final +``function_nodes``, ``n1_sink`` and ``n2_sink``, that process these resulting +matrices. The ``matrix_source`` is connected to both ``n1`` and ``n2``. The node ``n1`` +is connected to ``n1_sink``, and ``n2`` is connected to ``n2_sink``. In the lambda +expressions for ``n1`` and ``n2``, a ``parallel_for`` is used to apply the functions +to the elements of the matrix in parallel. The functions +``read_next_matrix``, ``f1``, ``f2``, ``consume_f1`` and ``consume_f2`` are not provided +below. + + +:: + + + graph g; + input_node< double * > matrix_source( g, [&]( oneapi::tbb::flow_control &fc ) -> double* { + double *a = read_next_matrix(); + if ( a ) { + return a; + } else { + fc.stop(); + return nullptr; + } + } ); + function_node< double *, double * > n1( g, unlimited, [&]( double *a ) -> double * { + double *b = new double[N]; + parallel_for( 0, N, [&](int i) { + b[i] = f1(a[i]); + } ); + return b; + } ); + function_node< double *, double * > n2( g, unlimited, [&]( double *a ) -> double * { + double *b = new double[N]; + parallel_for( 0, N, [&](int i) { + b[i] = f2(a[i]); + } ); + return b; + } ); + function_node< double *, double * > n1_sink( g, unlimited, + []( double *b ) -> double * { + return consume_f1(b); + } ); + function_node< double *, double * > n2_sink( g, unlimited, + []( double *b ) -> double * { + return consume_f2(b); + } ); + make_edge( matrix_source, n1 ); + make_edge( matrix_source, n2 ); + make_edge( n1, n1_sink ); + make_edge( n2, n2_sink ); + matrix_source.activate(); + g.wait_for_all(); + diff --git a/_sources/main/tbb_userguide/use_nested_flow_graphs.rst b/_sources/main/tbb_userguide/use_nested_flow_graphs.rst new file mode 100644 index 0000000000..55ed8d6c57 --- /dev/null +++ b/_sources/main/tbb_userguide/use_nested_flow_graphs.rst @@ -0,0 +1,132 @@ +.. _use_nested_flow_graphs: + +Use Nested Flow Graphs +====================== + + +In addition to nesting algorithms within a flow graph node, it is also +possible to nest flow graphs. For example, below there is a graph ``g`` with +two nodes, ``a`` and ``b``. When node ``a`` receives a message, it constructs and +executes an inner dependence graph. When node ``b`` receives a message, it +constructs and executes an inner data flow graph: + + +:: + + + graph g; + function_node< int, int > a( g, unlimited, []( int i ) -> int { + graph h; + node_t n1( h, [=]( msg_t ) { cout << "n1: " << i << "\n"; } ); + node_t n2( h, [=]( msg_t ) { cout << "n2: " << i << "\n"; } ); + node_t n3( h, [=]( msg_t ) { cout << "n3: " << i << "\n"; } ); + node_t n4( h, [=]( msg_t ) { cout << "n4: " << i << "\n"; } ); + make_edge( n1, n2 ); + make_edge( n1, n3 ); + make_edge( n2, n4 ); + make_edge( n3, n4 ); + n1.try_put(continue_msg()); + h.wait_for_all(); + return i; + } ); + function_node< int, int > b( g, unlimited, []( int i ) -> int { + graph h; + function_node< int, int > m1( h, unlimited, []( int j ) -> int { + cout << "m1: " << j << "\n"; + return j; + } ); + function_node< int, int > m2( h, unlimited, []( int j ) -> int { + cout << "m2: " << j << "\n"; + return j; + } ); + function_node< int, int > m3( h, unlimited, []( int j ) -> int { + cout << "m3: " << j << "\n"; + return j; + } ); + function_node< int, int > m4( h, unlimited, []( int j ) -> int { + cout << "m4: " << j << "\n"; + return j; + } ); + make_edge( m1, m2 ); + make_edge( m1, m3 ); + make_edge( m2, m4 ); + make_edge( m3, m4 ); + m1.try_put(i); + h.wait_for_all(); + return i; + } ); + make_edge( a, b ); + for ( int i = 0; i < 3; ++i ) { + a.try_put(i); + } + g.wait_for_all(); + + +If the nested graph remains unchanged in structure between invocations +of the node, it is redundant to construct it each time. Reconstructing +the graph only adds overhead to the execution. You can modify the +example above, for example, to have node ``b`` reuse a graph that is +persistent across its invocations: + + +:: + + + graph h; + function_node< int, int > m1( h, unlimited, []( int j ) -> int { + cout << "m1: " << j << "\n"; + return j; + } ); + function_node< int, int > m2( h, unlimited, []( int j ) -> int { + cout << "m2: " << j << "\n"; + return j; + } ); + function_node< int, int > m3( h, unlimited, []( int j ) -> int { + cout << "m3: " << j << "\n"; + return j; + } ); + function_node< int, int > m4( h, unlimited, []( int j ) -> int { + cout << "m4: " << j << "\n"; + return j; + } ); + make_edge( m1, m2 ); + make_edge( m1, m3 ); + make_edge( m2, m4 ); + make_edge( m3, m4 ); + + + graph g; + function_node< int, int > a( g, unlimited, []( int i ) -> int { + graph h; + node_t n1( h, [=]( msg_t ) { cout << "n1: " << i << "\n"; } ); + node_t n2( h, [=]( msg_t ) { cout << "n2: " << i << "\n"; } ); + node_t n3( h, [=]( msg_t ) { cout << "n3: " << i << "\n"; } ); + node_t n4( h, [=]( msg_t ) { cout << "n4: " << i << "\n"; } ); + make_edge( n1, n2 ); + make_edge( n1, n3 ); + make_edge( n2, n4 ); + make_edge( n3, n4 ); + n1.try_put(continue_msg()); + h.wait_for_all(); + return i; + } ); + function_node< int, int > b( g, unlimited, [&]( int i ) -> int { + m1.try_put(i); + h.wait_for_all(); // optional since h is not destroyed + return i; + } ); + make_edge( a, b ); + for ( int i = 0; i < 3; ++i ) { + a.try_put(i); + } + g.wait_for_all(); + + +It is only necessary to call ``h.wait_for_all()`` at the end of each +invocation of ``b``'s body in our modified code, if you wish for this ``b``'s +body to block until the inner graph is done. In the first implementation +of ``b``, it was necessary to call ``h.wait_for_all`` at the end of each +invocation since the graph was destroyed at the end of the scope. So it +would be valid in the body of ``b`` above to call ``m1.try_put(i)`` and then +return without waiting for ``h`` to become idle. + diff --git a/_sources/main/tbb_userguide/work_isolation.rst b/_sources/main/tbb_userguide/work_isolation.rst new file mode 100644 index 0000000000..29e1d9682d --- /dev/null +++ b/_sources/main/tbb_userguide/work_isolation.rst @@ -0,0 +1,139 @@ +.. _work_isolation: + +Work Isolation +============== + + +.. container:: section + + + In |full_name|, a thread waiting for a + group of tasks to complete might execute other available tasks. In + particular, when a parallel construct calls another parallel + construct, a thread can obtain a task from the outer-level construct + while waiting for completion of the inner-level one. + + + In the following example with two ``parallel_for`` calls, the call to + the second (nested) parallel loop blocks execution of the first + (outer) loop iteration: + + + :: + + + // The first parallel loop. + oneapi::tbb::parallel_for( 0, N1, []( int i ) { + // The second parallel loop. + oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); + } ); + + + The blocked thread is allowed to take tasks belonging to the first + parallel loop. As a result, two or more iterations of the outer loop + might be simultaneously assigned to the same thread. In other words, + in oneTBB execution of functions constituting a parallel construct is + *unsequenced* even within a single thread. In most cases, this + behavior is harmless or even beneficial because it does not restrict + parallelism available for the thread. + + + However, in some cases such unsequenced execution may result in + errors. For example, a thread-local variable might unexpectedly + change its value after a nested parallel construct: + + + :: + + + oneapi::tbb::enumerable_thread_specific ets; + oneapi::tbb::parallel_for( 0, N1, [&ets]( int i ) { + // Set a thread specific value + ets.local() = i; + oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); + // While executing the above parallel_for, the thread might have run iterations + // of the outer parallel_for, and so might have changed the thread specific value. + assert( ets.local()==i ); // The assertion may fail! + } ); + + + In other scenarios, the described behavior might lead to deadlocks + and other issues. In these cases, a stronger guarantee of execution + being sequenced within a thread is desired. For that, oneTBB provides + ways to *isolate* execution of a parallel construct, for its tasks to + not interfere with other simultaneously running tasks. + + + One of these ways is to execute the inner level loop in a separate + ``task_arena``: + + + :: + + + oneapi::tbb::enumerable_thread_specific ets; + oneapi::tbb::task_arena nested; + oneapi::tbb::parallel_for( 0, N1, [&]( int i ) { + // Set a thread specific value + ets.local() = i; + nested.execute( []{ + // Run the inner parallel_for in a separate arena to prevent the thread + // from taking tasks of the outer parallel_for. + oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); + } ); + assert( ets.local()==i ); // Valid assertion + } ); + + + However, using a separate arena for work isolation is not always + convenient, and might have noticeable overheads. To address these + shortcomings, oneTBB provides ``this_task_arena::isolate`` function + which runs a user-provided functor in isolation by restricting the + calling thread to process only tasks scheduled in the scope of the + functor (also called the isolation region). + + + When entered a task waiting call or a blocking parallel construct + inside an isolated region, a thread can only execute tasks spawned + within the region and their child tasks spawned by other threads. The + thread is prohibited from executing any outer level tasks or tasks + belonging to other isolated regions. + + + The isolation region imposes restrictions only upon the thread that + called it. Other threads running in the same task arena have no + restrictions on task selection unless isolated by a distinct call to + ``this_task_arena::isolate``. + + + The following example demonstrates the use of + ``this_task_arena::isolate`` to ensure that a thread-local variable + is not changed unexpectedly during the call to a nested parallel + construct. + + + :: + + + #include "oneapi/tbb/task_arena.h" + #include "oneapi/tbb/parallel_for.h" + #include "oneapi/tbb/enumerable_thread_specific.h" + #include + + + int main() { + const int N1 = 1000, N2 = 1000; + oneapi::tbb::enumerable_thread_specific ets; + oneapi::tbb::parallel_for( 0, N1, [&ets]( int i ) { + // Set a thread specific value + ets.local() = i; + // Run the second parallel loop in an isolated region to prevent the current thread + // from taking tasks related to the outer parallel loop. + oneapi::tbb::this_task_arena::isolate( []{ + oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); + } ); + assert( ets.local()==i ); // Valid assertion + } ); + return 0; + } + diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000000..7ebbd6d07b --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,914 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/custom.js b/_static/custom.js new file mode 100644 index 0000000000..a7d312de32 --- /dev/null +++ b/_static/custom.js @@ -0,0 +1,37 @@ +window.MathJax = { + TeX: { + Macros: { + src: '\\operatorname{src}', + srclayer: '\\operatorname{src\\_layer}', + srciter: '\\operatorname{src\\_iter}', + srciterc: '\\operatorname{src\\_iter\\_c}', + weights: '\\operatorname{weights}', + weightslayer: '\\operatorname{weights\\_layer}', + weightsiter: '\\operatorname{weights\\_iter}', + weightspeephole: '\\operatorname{weights\\_peephole}', + weightsprojection: '\\operatorname{weights\\_projection}', + bias: '\\operatorname{bias}', + dst: '\\operatorname{dst}', + dstlayer: '\\operatorname{dst\\_layer}', + dstiter: '\\operatorname{dst\\_iter}', + dstiterc: '\\operatorname{dst\\_iter\\_c}', + diffsrc: '\\operatorname{diff\\_src}', + diffsrclayer: '\\operatorname{diff\\_src\\_layer}', + diffsrciter: '\\operatorname{diff\\_src\\_iter}', + diffsrciterc: '\\operatorname{diff\\_src\\_iter\\_c}', + diffweights: '\\operatorname{diff\\_weights}', + diffweightslayer: '\\operatorname{diff\\_weights\\_layer}', + diffweightsiter: '\\operatorname{diff\\_weights\\_iter}', + diffweightspeephole: '\\operatorname{diff\\_weights\\_peephole}', + diffweightsprojection: '\\operatorname{diff\\_weights\\_projection}', + diffbias: '\\operatorname{diff\\_bias}', + diffdst: '\\operatorname{diff\\_dst}', + diffdstlayer: '\\operatorname{diff\\_dst\\_layer}', + diffdstiter: '\\operatorname{diff\\_dst\\_iter}', + diffdstiterc: '\\operatorname{diff\\_dst\\_iter\\_c}', + diffgamma: '\\operatorname{diff\\_\\gamma}', + diffbeta: '\\operatorname{diff\\_\\beta}', + workspace: '\\operatorname{workspace}' + } + } +} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000000..0398ebb9f0 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000000..dab586c0d2 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/favicons.png b/_static/favicons.png new file mode 100644 index 0000000000..f450376b19 Binary files /dev/null and b/_static/favicons.png differ diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000..a858a410e4 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/images/logo_binder.svg b/_static/images/logo_binder.svg new file mode 100644 index 0000000000..45fecf7511 --- /dev/null +++ b/_static/images/logo_binder.svg @@ -0,0 +1,19 @@ + + + + +logo + + + + + + + + diff --git a/_static/images/logo_colab.png b/_static/images/logo_colab.png new file mode 100644 index 0000000000..b7560ec216 Binary files /dev/null and b/_static/images/logo_colab.png differ diff --git a/_static/images/logo_deepnote.svg b/_static/images/logo_deepnote.svg new file mode 100644 index 0000000000..fa77ebfc25 --- /dev/null +++ b/_static/images/logo_deepnote.svg @@ -0,0 +1 @@ + diff --git a/_static/images/logo_jupyterhub.svg b/_static/images/logo_jupyterhub.svg new file mode 100644 index 0000000000..60cfe9f222 --- /dev/null +++ b/_static/images/logo_jupyterhub.svg @@ -0,0 +1 @@ +logo_jupyterhubHub diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000000..c7fe6c6faf --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,192 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/locales/ar/LC_MESSAGES/booktheme.po b/_static/locales/ar/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..248839ed6a --- /dev/null +++ b/_static/locales/ar/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "تنزيل ملف دفتر الملاحظات" + +msgid "Fullscreen mode" +msgstr "وضع ملء الشاشة" + +msgid "Source repository" +msgstr "مستودع المصدر" + +msgid "Print to PDF" +msgstr "طباعة إلى PDF" + +msgid "Copyright" +msgstr "حقوق النشر" + +msgid "Sphinx Book Theme" +msgstr "موضوع كتاب أبو الهول" + +msgid "By the" +msgstr "بواسطة" + +msgid "Download this page" +msgstr "قم بتنزيل هذه الصفحة" + +msgid "repository" +msgstr "مخزن" + +msgid "Download source file" +msgstr "تنزيل ملف المصدر" + +msgid "By" +msgstr "بواسطة" + +msgid "Toggle navigation" +msgstr "تبديل التنقل" + +msgid "next page" +msgstr "الصفحة التالية" + +msgid "Last updated on" +msgstr "آخر تحديث في" + +msgid "Contents" +msgstr "محتويات" + +msgid "Launch" +msgstr "إطلاق" + +msgid "Theme by the" +msgstr "موضوع بواسطة" + +msgid "Open an issue" +msgstr "افتح قضية" + +msgid "open issue" +msgstr "قضية مفتوحة" + +msgid "suggest edit" +msgstr "أقترح تحرير" + +msgid "Edit this page" +msgstr "قم بتحرير هذه الصفحة" + +msgid "previous page" +msgstr "الصفحة السابقة" diff --git a/_static/locales/bg/LC_MESSAGES/booktheme.po b/_static/locales/bg/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..d9e917281f --- /dev/null +++ b/_static/locales/bg/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: bg\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Изтеглете файла на бележника" + +msgid "Fullscreen mode" +msgstr "Режим на цял екран" + +msgid "Source repository" +msgstr "Хранилище на източника" + +msgid "Print to PDF" +msgstr "Печат в PDF" + +msgid "Copyright" +msgstr "Авторско право" + +msgid "Sphinx Book Theme" +msgstr "Тема на книгата Sphinx" + +msgid "By the" +msgstr "По" + +msgid "Download this page" +msgstr "Изтеглете тази страница" + +msgid "repository" +msgstr "хранилище" + +msgid "Download source file" +msgstr "Изтеглете изходния файл" + +msgid "By" +msgstr "От" + +msgid "Toggle navigation" +msgstr "Превключване на навигацията" + +msgid "next page" +msgstr "Следваща страница" + +msgid "Last updated on" +msgstr "Последна актуализация на" + +msgid "Contents" +msgstr "Съдържание" + +msgid "Launch" +msgstr "Стартиране" + +msgid "Theme by the" +msgstr "Тема от" + +msgid "Open an issue" +msgstr "Отворете проблем" + +msgid "open issue" +msgstr "отворен брой" + +msgid "suggest edit" +msgstr "предложи редактиране" + +msgid "Edit this page" +msgstr "Редактирайте тази страница" + +msgid "previous page" +msgstr "предишна страница" diff --git a/_static/locales/bn/LC_MESSAGES/booktheme.po b/_static/locales/bn/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..37e9e1784b --- /dev/null +++ b/_static/locales/bn/LC_MESSAGES/booktheme.po @@ -0,0 +1,63 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: bn\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "নোটবুক ফাইল ডাউনলোড করুন" + +msgid "Source repository" +msgstr "উত্স সংগ্রহস্থল" + +msgid "Print to PDF" +msgstr "পিডিএফ প্রিন্ট করুন" + +msgid "Copyright" +msgstr "কপিরাইট" + +msgid "Sphinx Book Theme" +msgstr "স্পিনিক্স বুক থিম" + +msgid "By the" +msgstr "দ্বারা" + +msgid "Download this page" +msgstr "এই পৃষ্ঠাটি ডাউনলোড করুন" + +msgid "Download source file" +msgstr "উত্স ফাইল ডাউনলোড করুন" + +msgid "By" +msgstr "দ্বারা" + +msgid "Toggle navigation" +msgstr "নেভিগেশন টগল করুন" + +msgid "next page" +msgstr "পরবর্তী পৃষ্ঠা" + +msgid "Last updated on" +msgstr "সর্বশেষ আপডেট" + +msgid "Launch" +msgstr "শুরু করা" + +msgid "Theme by the" +msgstr "থিম দ্বারা" + +msgid "Open an issue" +msgstr "একটি সমস্যা খুলুন" + +msgid "open issue" +msgstr "খোলা সমস্যা" + +msgid "Edit this page" +msgstr "এই পৃষ্ঠাটি সম্পাদনা করুন" + +msgid "previous page" +msgstr "আগের পৃষ্ঠা" diff --git a/_static/locales/ca/LC_MESSAGES/booktheme.po b/_static/locales/ca/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..156b73ca64 --- /dev/null +++ b/_static/locales/ca/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ca\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Descarregar fitxer de quadern" + +msgid "Source repository" +msgstr "Dipòsit de fonts" + +msgid "Print to PDF" +msgstr "Imprimeix a PDF" + +msgid "Copyright" +msgstr "Copyright" + +msgid "Sphinx Book Theme" +msgstr "Tema del llibre Esfinx" + +msgid "By the" +msgstr "Per la" + +msgid "Download this page" +msgstr "Descarregueu aquesta pàgina" + +msgid "Download source file" +msgstr "Baixeu el fitxer font" + +msgid "By" +msgstr "Per" + +msgid "Toggle navigation" +msgstr "Commuta la navegació" + +msgid "next page" +msgstr "pàgina següent" + +msgid "Last updated on" +msgstr "Darrera actualització el" + +msgid "Launch" +msgstr "Llançament" + +msgid "Theme by the" +msgstr "Tema del" + +msgid "Open an issue" +msgstr "Obriu un número" + +msgid "open issue" +msgstr "número obert" + +msgid "suggest edit" +msgstr "suggerir edició" + +msgid "Edit this page" +msgstr "Editeu aquesta pàgina" + +msgid "previous page" +msgstr "Pàgina anterior" diff --git a/_static/locales/cs/LC_MESSAGES/booktheme.po b/_static/locales/cs/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..36e1e6f214 --- /dev/null +++ b/_static/locales/cs/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: cs\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Stáhnout soubor poznámkového bloku" + +msgid "Fullscreen mode" +msgstr "Režim celé obrazovky" + +msgid "Source repository" +msgstr "Zdrojové úložiště" + +msgid "Print to PDF" +msgstr "Tisk do PDF" + +msgid "Copyright" +msgstr "autorská práva" + +msgid "Sphinx Book Theme" +msgstr "Téma knihy Sfinga" + +msgid "By the" +msgstr "Podle" + +msgid "Download this page" +msgstr "Stáhněte si tuto stránku" + +msgid "repository" +msgstr "úložiště" + +msgid "Download source file" +msgstr "Stáhněte si zdrojový soubor" + +msgid "By" +msgstr "Podle" + +msgid "Toggle navigation" +msgstr "Přepnout navigaci" + +msgid "next page" +msgstr "další strana" + +msgid "Last updated on" +msgstr "Naposledy aktualizováno" + +msgid "Contents" +msgstr "Obsah" + +msgid "Launch" +msgstr "Zahájení" + +msgid "Theme by the" +msgstr "Téma od" + +msgid "Open an issue" +msgstr "Otevřete problém" + +msgid "open issue" +msgstr "otevřené číslo" + +msgid "suggest edit" +msgstr "navrhnout úpravy" + +msgid "Edit this page" +msgstr "Upravit tuto stránku" + +msgid "previous page" +msgstr "předchozí stránka" diff --git a/_static/locales/da/LC_MESSAGES/booktheme.po b/_static/locales/da/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..a43416421a --- /dev/null +++ b/_static/locales/da/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Download notesbog-fil" + +msgid "Fullscreen mode" +msgstr "Fuldskærmstilstand" + +msgid "Source repository" +msgstr "Kildelager" + +msgid "Print to PDF" +msgstr "Udskriv til PDF" + +msgid "Copyright" +msgstr "ophavsret" + +msgid "Sphinx Book Theme" +msgstr "Sphinx bogtema" + +msgid "By the" +msgstr "Ved" + +msgid "Download this page" +msgstr "Download denne side" + +msgid "repository" +msgstr "lager" + +msgid "Download source file" +msgstr "Download kildefil" + +msgid "By" +msgstr "Ved" + +msgid "Toggle navigation" +msgstr "Skift navigation" + +msgid "next page" +msgstr "Næste side" + +msgid "Last updated on" +msgstr "Sidst opdateret den" + +msgid "Contents" +msgstr "Indhold" + +msgid "Launch" +msgstr "Start" + +msgid "Theme by the" +msgstr "Tema af" + +msgid "Open an issue" +msgstr "Åbn et problem" + +msgid "open issue" +msgstr "åbent nummer" + +msgid "suggest edit" +msgstr "foreslå redigering" + +msgid "Edit this page" +msgstr "Rediger denne side" + +msgid "previous page" +msgstr "forrige side" diff --git a/_static/locales/de/LC_MESSAGES/booktheme.po b/_static/locales/de/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..43dae9c5f3 --- /dev/null +++ b/_static/locales/de/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Notebook-Datei herunterladen" + +msgid "Fullscreen mode" +msgstr "Vollbildmodus" + +msgid "Source repository" +msgstr "Quell-Repository" + +msgid "Print to PDF" +msgstr "In PDF drucken" + +msgid "Copyright" +msgstr "Urheberrechte ©" + +msgid "Sphinx Book Theme" +msgstr "Sphinx-Buch-Thema" + +msgid "By the" +msgstr "Bis zum" + +msgid "Download this page" +msgstr "Laden Sie diese Seite herunter" + +msgid "repository" +msgstr "Repository" + +msgid "Download source file" +msgstr "Quelldatei herunterladen" + +msgid "By" +msgstr "Durch" + +msgid "Toggle navigation" +msgstr "Navigation umschalten" + +msgid "next page" +msgstr "Nächste Seite" + +msgid "Last updated on" +msgstr "Zuletzt aktualisiert am" + +msgid "Contents" +msgstr "Inhalt" + +msgid "Launch" +msgstr "Starten" + +msgid "Theme by the" +msgstr "Thema von der" + +msgid "Open an issue" +msgstr "Öffnen Sie ein Problem" + +msgid "open issue" +msgstr "offenes Thema" + +msgid "suggest edit" +msgstr "vorschlagen zu bearbeiten" + +msgid "Edit this page" +msgstr "Bearbeite diese Seite" + +msgid "previous page" +msgstr "vorherige Seite" diff --git a/_static/locales/el/LC_MESSAGES/booktheme.po b/_static/locales/el/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..08f576e7e3 --- /dev/null +++ b/_static/locales/el/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: el\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Λήψη αρχείου σημειωματάριου" + +msgid "Fullscreen mode" +msgstr "ΛΕΙΤΟΥΡΓΙΑ ΠΛΗΡΟΥΣ ΟΘΟΝΗΣ" + +msgid "Source repository" +msgstr "Αποθήκη πηγής" + +msgid "Print to PDF" +msgstr "Εκτύπωση σε PDF" + +msgid "Copyright" +msgstr "Πνευματική ιδιοκτησία" + +msgid "Sphinx Book Theme" +msgstr "Θέμα βιβλίου Sphinx" + +msgid "By the" +msgstr "Από το" + +msgid "Download this page" +msgstr "Λήψη αυτής της σελίδας" + +msgid "repository" +msgstr "αποθήκη" + +msgid "Download source file" +msgstr "Λήψη αρχείου προέλευσης" + +msgid "By" +msgstr "Με" + +msgid "Toggle navigation" +msgstr "Εναλλαγή πλοήγησης" + +msgid "next page" +msgstr "επόμενη σελίδα" + +msgid "Last updated on" +msgstr "Τελευταία ενημέρωση στις" + +msgid "Contents" +msgstr "Περιεχόμενα" + +msgid "Launch" +msgstr "Εκτόξευση" + +msgid "Theme by the" +msgstr "Θέμα από το" + +msgid "Open an issue" +msgstr "Ανοίξτε ένα ζήτημα" + +msgid "open issue" +msgstr "ανοιχτό ζήτημα" + +msgid "suggest edit" +msgstr "προτείνω επεξεργασία" + +msgid "Edit this page" +msgstr "Επεξεργαστείτε αυτήν τη σελίδα" + +msgid "previous page" +msgstr "προηγούμενη σελίδα" diff --git a/_static/locales/eo/LC_MESSAGES/booktheme.po b/_static/locales/eo/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..93eeb04794 --- /dev/null +++ b/_static/locales/eo/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: eo\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Elŝutu kajeran dosieron" + +msgid "Fullscreen mode" +msgstr "Plenekrana reĝimo" + +msgid "Source repository" +msgstr "Fonto-deponejo" + +msgid "Print to PDF" +msgstr "Presi al PDF" + +msgid "Copyright" +msgstr "Kopirajto" + +msgid "Sphinx Book Theme" +msgstr "Sfinksa Libro-Temo" + +msgid "By the" +msgstr "Per la" + +msgid "Download this page" +msgstr "Elŝutu ĉi tiun paĝon" + +msgid "repository" +msgstr "deponejo" + +msgid "Download source file" +msgstr "Elŝutu fontodosieron" + +msgid "By" +msgstr "De" + +msgid "Toggle navigation" +msgstr "Ŝalti navigadon" + +msgid "next page" +msgstr "sekva paĝo" + +msgid "Last updated on" +msgstr "Laste ĝisdatigita la" + +msgid "Contents" +msgstr "Enhavo" + +msgid "Launch" +msgstr "Lanĉo" + +msgid "Theme by the" +msgstr "Temo de la" + +msgid "Open an issue" +msgstr "Malfermu numeron" + +msgid "open issue" +msgstr "malferma numero" + +msgid "suggest edit" +msgstr "sugesti redaktadon" + +msgid "Edit this page" +msgstr "Redaktu ĉi tiun paĝon" + +msgid "previous page" +msgstr "antaŭa paĝo" diff --git a/_static/locales/es/LC_MESSAGES/booktheme.po b/_static/locales/es/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..9b1e39e673 --- /dev/null +++ b/_static/locales/es/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Descargar archivo de cuaderno" + +msgid "Fullscreen mode" +msgstr "Modo de pantalla completa" + +msgid "Source repository" +msgstr "Repositorio de origen" + +msgid "Print to PDF" +msgstr "Imprimir en PDF" + +msgid "Copyright" +msgstr "Derechos de autor" + +msgid "Sphinx Book Theme" +msgstr "Tema del libro de la esfinge" + +msgid "By the" +msgstr "Por el" + +msgid "Download this page" +msgstr "Descarga esta pagina" + +msgid "repository" +msgstr "repositorio" + +msgid "Download source file" +msgstr "Descargar archivo fuente" + +msgid "By" +msgstr "Por" + +msgid "Toggle navigation" +msgstr "Navegación de palanca" + +msgid "next page" +msgstr "siguiente página" + +msgid "Last updated on" +msgstr "Ultima actualización en" + +msgid "Contents" +msgstr "Contenido" + +msgid "Launch" +msgstr "Lanzamiento" + +msgid "Theme by the" +msgstr "Tema por el" + +msgid "Open an issue" +msgstr "Abrir un problema" + +msgid "open issue" +msgstr "Tema abierto" + +msgid "suggest edit" +msgstr "sugerir editar" + +msgid "Edit this page" +msgstr "Edita esta página" + +msgid "previous page" +msgstr "pagina anterior" diff --git a/_static/locales/et/LC_MESSAGES/booktheme.po b/_static/locales/et/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..706b4d9ffa --- /dev/null +++ b/_static/locales/et/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: et\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Laadige sülearvuti fail alla" + +msgid "Fullscreen mode" +msgstr "Täisekraanirežiim" + +msgid "Source repository" +msgstr "Allikahoidla" + +msgid "Print to PDF" +msgstr "Prindi PDF-i" + +msgid "Copyright" +msgstr "Autoriõigus" + +msgid "Sphinx Book Theme" +msgstr "Sfinksiraamatu teema" + +msgid "By the" +msgstr "Autor" + +msgid "Download this page" +msgstr "Laadige see leht alla" + +msgid "repository" +msgstr "hoidla" + +msgid "Download source file" +msgstr "Laadige alla lähtefail" + +msgid "By" +msgstr "Kõrval" + +msgid "Toggle navigation" +msgstr "Lülita navigeerimine sisse" + +msgid "next page" +msgstr "järgmine leht" + +msgid "Last updated on" +msgstr "Viimati uuendatud" + +msgid "Contents" +msgstr "Sisu" + +msgid "Launch" +msgstr "Käivitage" + +msgid "Theme by the" +msgstr "Teema" + +msgid "Open an issue" +msgstr "Avage probleem" + +msgid "open issue" +msgstr "avatud küsimus" + +msgid "suggest edit" +msgstr "soovita muuta" + +msgid "Edit this page" +msgstr "Muutke seda lehte" + +msgid "previous page" +msgstr "eelmine leht" diff --git a/_static/locales/fi/LC_MESSAGES/booktheme.po b/_static/locales/fi/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..5f2a8f8dcb --- /dev/null +++ b/_static/locales/fi/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Lataa muistikirjatiedosto" + +msgid "Fullscreen mode" +msgstr "Koko näytön tila" + +msgid "Source repository" +msgstr "Lähteen arkisto" + +msgid "Print to PDF" +msgstr "Tulosta PDF-tiedostoon" + +msgid "Copyright" +msgstr "Tekijänoikeus" + +msgid "Sphinx Book Theme" +msgstr "Sphinx-kirjan teema" + +msgid "By the" +msgstr "Mukaan" + +msgid "Download this page" +msgstr "Lataa tämä sivu" + +msgid "repository" +msgstr "arkisto" + +msgid "Download source file" +msgstr "Lataa lähdetiedosto" + +msgid "By" +msgstr "Tekijä" + +msgid "Toggle navigation" +msgstr "Vaihda navigointia" + +msgid "next page" +msgstr "seuraava sivu" + +msgid "Last updated on" +msgstr "Viimeksi päivitetty" + +msgid "Contents" +msgstr "Sisällys" + +msgid "Launch" +msgstr "Tuoda markkinoille" + +msgid "Theme by the" +msgstr "Teeman tekijä" + +msgid "Open an issue" +msgstr "Avaa ongelma" + +msgid "open issue" +msgstr "avoin ongelma" + +msgid "suggest edit" +msgstr "ehdottaa muokkausta" + +msgid "Edit this page" +msgstr "Muokkaa tätä sivua" + +msgid "previous page" +msgstr "Edellinen sivu" diff --git a/_static/locales/fr/LC_MESSAGES/booktheme.po b/_static/locales/fr/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..493bfbb759 --- /dev/null +++ b/_static/locales/fr/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Télécharger le fichier notebook" + +msgid "Fullscreen mode" +msgstr "Mode plein écran" + +msgid "Source repository" +msgstr "Dépôt source" + +msgid "Print to PDF" +msgstr "Imprimer au format PDF" + +msgid "Copyright" +msgstr "droits d'auteur" + +msgid "Sphinx Book Theme" +msgstr "Thème du livre Sphinx" + +msgid "By the" +msgstr "Par le" + +msgid "Download this page" +msgstr "Téléchargez cette page" + +msgid "repository" +msgstr "dépôt" + +msgid "Download source file" +msgstr "Télécharger le fichier source" + +msgid "By" +msgstr "Par" + +msgid "Toggle navigation" +msgstr "Basculer la navigation" + +msgid "next page" +msgstr "page suivante" + +msgid "Last updated on" +msgstr "Dernière mise à jour le" + +msgid "Contents" +msgstr "Contenu" + +msgid "Launch" +msgstr "lancement" + +msgid "Theme by the" +msgstr "Thème par le" + +msgid "Open an issue" +msgstr "Ouvrez un problème" + +msgid "open issue" +msgstr "signaler un problème" + +msgid "suggest edit" +msgstr "suggestion de modification" + +msgid "Edit this page" +msgstr "Modifier cette page" + +msgid "previous page" +msgstr "page précédente" diff --git a/_static/locales/hr/LC_MESSAGES/booktheme.po b/_static/locales/hr/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..23c5ee3686 --- /dev/null +++ b/_static/locales/hr/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hr\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Preuzmi datoteku bilježnice" + +msgid "Fullscreen mode" +msgstr "Način preko cijelog zaslona" + +msgid "Source repository" +msgstr "Izvorno spremište" + +msgid "Print to PDF" +msgstr "Ispis u PDF" + +msgid "Copyright" +msgstr "Autorska prava" + +msgid "Sphinx Book Theme" +msgstr "Tema knjige Sphinx" + +msgid "By the" +msgstr "Od strane" + +msgid "Download this page" +msgstr "Preuzmite ovu stranicu" + +msgid "repository" +msgstr "spremište" + +msgid "Download source file" +msgstr "Preuzmi izvornu datoteku" + +msgid "By" +msgstr "Po" + +msgid "Toggle navigation" +msgstr "Uključi / isključi navigaciju" + +msgid "next page" +msgstr "sljedeća stranica" + +msgid "Last updated on" +msgstr "Posljednje ažuriranje:" + +msgid "Contents" +msgstr "Sadržaj" + +msgid "Launch" +msgstr "Pokrenite" + +msgid "Theme by the" +msgstr "Tema autora" + +msgid "Open an issue" +msgstr "Otvorite izdanje" + +msgid "open issue" +msgstr "otvoreno izdanje" + +msgid "suggest edit" +msgstr "predloži uređivanje" + +msgid "Edit this page" +msgstr "Uredite ovu stranicu" + +msgid "previous page" +msgstr "Prethodna stranica" diff --git a/_static/locales/id/LC_MESSAGES/booktheme.po b/_static/locales/id/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..2fb9b7a874 --- /dev/null +++ b/_static/locales/id/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: id\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Unduh file notebook" + +msgid "Fullscreen mode" +msgstr "Mode layar penuh" + +msgid "Source repository" +msgstr "Repositori sumber" + +msgid "Print to PDF" +msgstr "Cetak ke PDF" + +msgid "Copyright" +msgstr "hak cipta" + +msgid "Sphinx Book Theme" +msgstr "Tema Buku Sphinx" + +msgid "By the" +msgstr "Oleh" + +msgid "Download this page" +msgstr "Unduh halaman ini" + +msgid "repository" +msgstr "gudang" + +msgid "Download source file" +msgstr "Unduh file sumber" + +msgid "By" +msgstr "Oleh" + +msgid "Toggle navigation" +msgstr "Alihkan navigasi" + +msgid "next page" +msgstr "halaman selanjutnya" + +msgid "Last updated on" +msgstr "Terakhir diperbarui saat" + +msgid "Contents" +msgstr "Isi" + +msgid "Launch" +msgstr "Meluncurkan" + +msgid "Theme by the" +msgstr "Tema oleh" + +msgid "Open an issue" +msgstr "Buka masalah" + +msgid "open issue" +msgstr "masalah terbuka" + +msgid "suggest edit" +msgstr "menyarankan edit" + +msgid "Edit this page" +msgstr "Edit halaman ini" + +msgid "previous page" +msgstr "halaman sebelumnya" diff --git a/_static/locales/it/LC_MESSAGES/booktheme.po b/_static/locales/it/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..5b752c9e8f --- /dev/null +++ b/_static/locales/it/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Scarica il file del taccuino" + +msgid "Fullscreen mode" +msgstr "Modalità schermo intero" + +msgid "Source repository" +msgstr "Repository di origine" + +msgid "Print to PDF" +msgstr "Stampa in PDF" + +msgid "Copyright" +msgstr "Diritto d'autore" + +msgid "Sphinx Book Theme" +msgstr "Tema del libro della Sfinge" + +msgid "By the" +msgstr "Dal" + +msgid "Download this page" +msgstr "Scarica questa pagina" + +msgid "repository" +msgstr "repository" + +msgid "Download source file" +msgstr "Scarica il file sorgente" + +msgid "By" +msgstr "Di" + +msgid "Toggle navigation" +msgstr "Attiva / disattiva la navigazione" + +msgid "next page" +msgstr "pagina successiva" + +msgid "Last updated on" +msgstr "Ultimo aggiornamento il" + +msgid "Contents" +msgstr "Contenuti" + +msgid "Launch" +msgstr "Lanciare" + +msgid "Theme by the" +msgstr "Tema di" + +msgid "Open an issue" +msgstr "Apri un problema" + +msgid "open issue" +msgstr "questione aperta" + +msgid "suggest edit" +msgstr "suggerisci modifica" + +msgid "Edit this page" +msgstr "Modifica questa pagina" + +msgid "previous page" +msgstr "pagina precedente" diff --git a/_static/locales/iw/LC_MESSAGES/booktheme.po b/_static/locales/iw/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..98fa583e79 --- /dev/null +++ b/_static/locales/iw/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: iw\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "הורד קובץ מחברת" + +msgid "Fullscreen mode" +msgstr "מצב מסך מלא" + +msgid "Source repository" +msgstr "מאגר המקורות" + +msgid "Print to PDF" +msgstr "הדפס לקובץ PDF" + +msgid "Copyright" +msgstr "זכויות יוצרים" + +msgid "Sphinx Book Theme" +msgstr "נושא ספר ספינקס" + +msgid "By the" +msgstr "דרך" + +msgid "Download this page" +msgstr "הורד דף זה" + +msgid "repository" +msgstr "מאגר" + +msgid "Download source file" +msgstr "הורד את קובץ המקור" + +msgid "By" +msgstr "על ידי" + +msgid "Toggle navigation" +msgstr "החלף ניווט" + +msgid "next page" +msgstr "עמוד הבא" + +msgid "Last updated on" +msgstr "עודכן לאחרונה ב" + +msgid "Contents" +msgstr "תוכן" + +msgid "Launch" +msgstr "לְהַשִׁיק" + +msgid "Theme by the" +msgstr "נושא מאת" + +msgid "Open an issue" +msgstr "פתח גיליון" + +msgid "open issue" +msgstr "בעיה פתוחה" + +msgid "suggest edit" +msgstr "מציע לערוך" + +msgid "Edit this page" +msgstr "ערוך דף זה" + +msgid "previous page" +msgstr "עמוד קודם" diff --git a/_static/locales/ja/LC_MESSAGES/booktheme.po b/_static/locales/ja/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..255ec18753 --- /dev/null +++ b/_static/locales/ja/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ja\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "ノートブックファイルをダウンロード" + +msgid "Fullscreen mode" +msgstr "全画面モード" + +msgid "Source repository" +msgstr "ソースリポジトリ" + +msgid "Print to PDF" +msgstr "PDFに印刷" + +msgid "Copyright" +msgstr "Copyright" + +msgid "Sphinx Book Theme" +msgstr "スフィンクスの本のテーマ" + +msgid "By the" +msgstr "によって" + +msgid "Download this page" +msgstr "このページをダウンロード" + +msgid "repository" +msgstr "リポジトリ" + +msgid "Download source file" +msgstr "ソースファイルをダウンロード" + +msgid "By" +msgstr "著者" + +msgid "Toggle navigation" +msgstr "ナビゲーションを切り替え" + +msgid "next page" +msgstr "次のページ" + +msgid "Last updated on" +msgstr "最終更新日" + +msgid "Contents" +msgstr "目次" + +msgid "Launch" +msgstr "起動" + +msgid "Theme by the" +msgstr "のテーマ" + +msgid "Open an issue" +msgstr "問題を報告" + +msgid "open issue" +msgstr "未解決の問題" + +msgid "suggest edit" +msgstr "編集を提案する" + +msgid "Edit this page" +msgstr "このページを編集" + +msgid "previous page" +msgstr "前のページ" diff --git a/_static/locales/ko/LC_MESSAGES/booktheme.po b/_static/locales/ko/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..6d602356f5 --- /dev/null +++ b/_static/locales/ko/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "노트북 파일 다운로드" + +msgid "Fullscreen mode" +msgstr "전체 화면으로보기" + +msgid "Source repository" +msgstr "소스 저장소" + +msgid "Print to PDF" +msgstr "PDF로 인쇄" + +msgid "Copyright" +msgstr "저작권" + +msgid "Sphinx Book Theme" +msgstr "스핑크스 도서 테마" + +msgid "By the" +msgstr "에 의해" + +msgid "Download this page" +msgstr "이 페이지 다운로드" + +msgid "repository" +msgstr "저장소" + +msgid "Download source file" +msgstr "소스 파일 다운로드" + +msgid "By" +msgstr "으로" + +msgid "Toggle navigation" +msgstr "탐색 전환" + +msgid "next page" +msgstr "다음 페이지" + +msgid "Last updated on" +msgstr "마지막 업데이트" + +msgid "Contents" +msgstr "내용" + +msgid "Launch" +msgstr "시작하다" + +msgid "Theme by the" +msgstr "테마별" + +msgid "Open an issue" +msgstr "이슈 열기" + +msgid "open issue" +msgstr "열린 문제" + +msgid "suggest edit" +msgstr "편집 제안" + +msgid "Edit this page" +msgstr "이 페이지 편집" + +msgid "previous page" +msgstr "이전 페이지" diff --git a/_static/locales/lt/LC_MESSAGES/booktheme.po b/_static/locales/lt/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..9779fd9545 --- /dev/null +++ b/_static/locales/lt/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lt\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Atsisiųsti nešiojamojo kompiuterio failą" + +msgid "Fullscreen mode" +msgstr "Pilno ekrano režimas" + +msgid "Source repository" +msgstr "Šaltinio saugykla" + +msgid "Print to PDF" +msgstr "Spausdinti į PDF" + +msgid "Copyright" +msgstr "Autorių teisės" + +msgid "Sphinx Book Theme" +msgstr "Sfinkso knygos tema" + +msgid "By the" +msgstr "Prie" + +msgid "Download this page" +msgstr "Atsisiųskite šį puslapį" + +msgid "repository" +msgstr "saugykla" + +msgid "Download source file" +msgstr "Atsisiųsti šaltinio failą" + +msgid "By" +msgstr "Iki" + +msgid "Toggle navigation" +msgstr "Perjungti naršymą" + +msgid "next page" +msgstr "Kitas puslapis" + +msgid "Last updated on" +msgstr "Paskutinį kartą atnaujinta" + +msgid "Contents" +msgstr "Turinys" + +msgid "Launch" +msgstr "Paleiskite" + +msgid "Theme by the" +msgstr "Tema" + +msgid "Open an issue" +msgstr "Atidarykite problemą" + +msgid "open issue" +msgstr "atviras klausimas" + +msgid "suggest edit" +msgstr "pasiūlyti redaguoti" + +msgid "Edit this page" +msgstr "Redaguoti šį puslapį" + +msgid "previous page" +msgstr "Ankstesnis puslapis" diff --git a/_static/locales/lv/LC_MESSAGES/booktheme.po b/_static/locales/lv/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..605b47bc72 --- /dev/null +++ b/_static/locales/lv/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lv\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Lejupielādēt piezīmju grāmatiņu" + +msgid "Fullscreen mode" +msgstr "Pilnekrāna režīms" + +msgid "Source repository" +msgstr "Avota krātuve" + +msgid "Print to PDF" +msgstr "Drukāt PDF formātā" + +msgid "Copyright" +msgstr "Autortiesības" + +msgid "Sphinx Book Theme" +msgstr "Sfinksa grāmatas tēma" + +msgid "By the" +msgstr "Ar" + +msgid "Download this page" +msgstr "Lejupielādējiet šo lapu" + +msgid "repository" +msgstr "krātuve" + +msgid "Download source file" +msgstr "Lejupielādēt avota failu" + +msgid "By" +msgstr "Autors" + +msgid "Toggle navigation" +msgstr "Pārslēgt navigāciju" + +msgid "next page" +msgstr "nākamā lapaspuse" + +msgid "Last updated on" +msgstr "Pēdējoreiz atjaunināts" + +msgid "Contents" +msgstr "Saturs" + +msgid "Launch" +msgstr "Uzsākt" + +msgid "Theme by the" +msgstr "Autora tēma" + +msgid "Open an issue" +msgstr "Atveriet problēmu" + +msgid "open issue" +msgstr "atklāts jautājums" + +msgid "suggest edit" +msgstr "ieteikt rediģēt" + +msgid "Edit this page" +msgstr "Rediģēt šo lapu" + +msgid "previous page" +msgstr "iepriekšējā lapa" diff --git a/_static/locales/ml/LC_MESSAGES/booktheme.po b/_static/locales/ml/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..104984e75d --- /dev/null +++ b/_static/locales/ml/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ml\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "നോട്ട്ബുക്ക് ഫയൽ ഡൺലോഡ് ചെയ്യുക" + +msgid "Source repository" +msgstr "ഉറവിട ശേഖരം" + +msgid "Print to PDF" +msgstr "PDF- ലേക്ക് പ്രിന്റുചെയ്യുക" + +msgid "Copyright" +msgstr "പകർപ്പവകാശം" + +msgid "Sphinx Book Theme" +msgstr "സ്ഫിങ്ക്സ് പുസ്തക തീം" + +msgid "By the" +msgstr "എഴുതിയത്" + +msgid "Download this page" +msgstr "ഈ പേജ് ഡൗൺലോഡുചെയ്യുക" + +msgid "Download source file" +msgstr "ഉറവിട ഫയൽ ഡൗൺലോഡുചെയ്യുക" + +msgid "By" +msgstr "എഴുതിയത്" + +msgid "Toggle navigation" +msgstr "നാവിഗേഷൻ ടോഗിൾ ചെയ്യുക" + +msgid "next page" +msgstr "അടുത്ത പേജ്" + +msgid "Last updated on" +msgstr "അവസാനം അപ്‌ഡേറ്റുചെയ്‌തത്" + +msgid "Launch" +msgstr "സമാരംഭിക്കുക" + +msgid "Theme by the" +msgstr "പ്രമേയം" + +msgid "Open an issue" +msgstr "ഒരു പ്രശ്നം തുറക്കുക" + +msgid "open issue" +msgstr "തുറന്ന പ്രശ്നം" + +msgid "suggest edit" +msgstr "എഡിറ്റുചെയ്യാൻ നിർദ്ദേശിക്കുക" + +msgid "Edit this page" +msgstr "ഈ പേജ് എഡിറ്റുചെയ്യുക" + +msgid "previous page" +msgstr "മുൻപത്തെ താൾ" diff --git a/_static/locales/mr/LC_MESSAGES/booktheme.po b/_static/locales/mr/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..c3d2c36d7c --- /dev/null +++ b/_static/locales/mr/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: mr\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "नोटबुक फाईल डाउनलोड करा" + +msgid "Source repository" +msgstr "स्त्रोत भांडार" + +msgid "Print to PDF" +msgstr "पीडीएफवर मुद्रित करा" + +msgid "Copyright" +msgstr "कॉपीराइट" + +msgid "Sphinx Book Theme" +msgstr "स्फिंक्स बुक थीम" + +msgid "By the" +msgstr "द्वारा" + +msgid "Download this page" +msgstr "हे पृष्ठ डाउनलोड करा" + +msgid "Download source file" +msgstr "स्त्रोत फाइल डाउनलोड करा" + +msgid "By" +msgstr "द्वारा" + +msgid "Toggle navigation" +msgstr "नेव्हिगेशन टॉगल करा" + +msgid "next page" +msgstr "पुढील पृष्ठ" + +msgid "Last updated on" +msgstr "अखेरचे अद्यतनित" + +msgid "Launch" +msgstr "लाँच करा" + +msgid "Theme by the" +msgstr "द्वारा थीम" + +msgid "Open an issue" +msgstr "एक मुद्दा उघडा" + +msgid "open issue" +msgstr "खुला मुद्दा" + +msgid "suggest edit" +msgstr "संपादन सुचवा" + +msgid "Edit this page" +msgstr "हे पृष्ठ संपादित करा" + +msgid "previous page" +msgstr "मागील पान" diff --git a/_static/locales/ms/LC_MESSAGES/booktheme.po b/_static/locales/ms/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..d0209ab301 --- /dev/null +++ b/_static/locales/ms/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ms\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Muat turun fail buku nota" + +msgid "Source repository" +msgstr "Repositori sumber" + +msgid "Print to PDF" +msgstr "Cetak ke PDF" + +msgid "Copyright" +msgstr "hak cipta" + +msgid "Sphinx Book Theme" +msgstr "Tema Buku Sphinx" + +msgid "By the" +msgstr "Oleh" + +msgid "Download this page" +msgstr "Muat turun halaman ini" + +msgid "Download source file" +msgstr "Muat turun fail sumber" + +msgid "By" +msgstr "Oleh" + +msgid "Toggle navigation" +msgstr "Togol navigasi" + +msgid "next page" +msgstr "muka surat seterusnya" + +msgid "Last updated on" +msgstr "Terakhir dikemas kini pada" + +msgid "Launch" +msgstr "Lancarkan" + +msgid "Theme by the" +msgstr "Tema oleh" + +msgid "Open an issue" +msgstr "Buka masalah" + +msgid "open issue" +msgstr "isu terbuka" + +msgid "suggest edit" +msgstr "cadangkan edit" + +msgid "Edit this page" +msgstr "Edit halaman ini" + +msgid "previous page" +msgstr "halaman sebelumnya" diff --git a/_static/locales/nl/LC_MESSAGES/booktheme.po b/_static/locales/nl/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..c5529a0ceb --- /dev/null +++ b/_static/locales/nl/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Download notebookbestand" + +msgid "Fullscreen mode" +msgstr "Volledig scherm" + +msgid "Source repository" +msgstr "Bronopslagplaats" + +msgid "Print to PDF" +msgstr "Afdrukken naar pdf" + +msgid "Copyright" +msgstr "auteursrechten" + +msgid "Sphinx Book Theme" +msgstr "Sphinx-boekthema" + +msgid "By the" +msgstr "Door de" + +msgid "Download this page" +msgstr "Download deze pagina" + +msgid "repository" +msgstr "repository" + +msgid "Download source file" +msgstr "Download het bronbestand" + +msgid "By" +msgstr "Door" + +msgid "Toggle navigation" +msgstr "Schakel navigatie" + +msgid "next page" +msgstr "volgende bladzijde" + +msgid "Last updated on" +msgstr "Laatst geupdate op" + +msgid "Contents" +msgstr "Inhoud" + +msgid "Launch" +msgstr "Lancering" + +msgid "Theme by the" +msgstr "Thema door de" + +msgid "Open an issue" +msgstr "Open een probleem" + +msgid "open issue" +msgstr "open probleem" + +msgid "suggest edit" +msgstr "suggereren bewerken" + +msgid "Edit this page" +msgstr "bewerk deze pagina" + +msgid "previous page" +msgstr "vorige pagina" diff --git a/_static/locales/no/LC_MESSAGES/booktheme.po b/_static/locales/no/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..11ddbf4486 --- /dev/null +++ b/_static/locales/no/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: no\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Last ned notatbokfilen" + +msgid "Fullscreen mode" +msgstr "Fullskjerm-modus" + +msgid "Source repository" +msgstr "Kildedepot" + +msgid "Print to PDF" +msgstr "Skriv ut til PDF" + +msgid "Copyright" +msgstr "opphavsrett" + +msgid "Sphinx Book Theme" +msgstr "Sphinx boktema" + +msgid "By the" +msgstr "Ved" + +msgid "Download this page" +msgstr "Last ned denne siden" + +msgid "repository" +msgstr "oppbevaringssted" + +msgid "Download source file" +msgstr "Last ned kildefilen" + +msgid "By" +msgstr "Av" + +msgid "Toggle navigation" +msgstr "Bytt navigasjon" + +msgid "next page" +msgstr "neste side" + +msgid "Last updated on" +msgstr "Sist oppdatert den" + +msgid "Contents" +msgstr "Innhold" + +msgid "Launch" +msgstr "Start" + +msgid "Theme by the" +msgstr "Tema av" + +msgid "Open an issue" +msgstr "Åpne et problem" + +msgid "open issue" +msgstr "åpent nummer" + +msgid "suggest edit" +msgstr "foreslå redigering" + +msgid "Edit this page" +msgstr "Rediger denne siden" + +msgid "previous page" +msgstr "forrige side" diff --git a/_static/locales/pl/LC_MESSAGES/booktheme.po b/_static/locales/pl/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..5132cd62f2 --- /dev/null +++ b/_static/locales/pl/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Pobierz plik notatnika" + +msgid "Fullscreen mode" +msgstr "Pełny ekran" + +msgid "Source repository" +msgstr "Repozytorium źródłowe" + +msgid "Print to PDF" +msgstr "Drukuj do PDF" + +msgid "Copyright" +msgstr "prawa autorskie" + +msgid "Sphinx Book Theme" +msgstr "Motyw książki Sphinx" + +msgid "By the" +msgstr "Przez" + +msgid "Download this page" +msgstr "Pobierz tę stronę" + +msgid "repository" +msgstr "magazyn" + +msgid "Download source file" +msgstr "Pobierz plik źródłowy" + +msgid "By" +msgstr "Przez" + +msgid "Toggle navigation" +msgstr "Przełącz nawigację" + +msgid "next page" +msgstr "Następna strona" + +msgid "Last updated on" +msgstr "Ostatnia aktualizacja" + +msgid "Contents" +msgstr "Zawartość" + +msgid "Launch" +msgstr "Uruchomić" + +msgid "Theme by the" +msgstr "Motyw autorstwa" + +msgid "Open an issue" +msgstr "Otwórz problem" + +msgid "open issue" +msgstr "otwarty problem" + +msgid "suggest edit" +msgstr "zaproponuj edycję" + +msgid "Edit this page" +msgstr "Edytuj tę strone" + +msgid "previous page" +msgstr "Poprzednia strona" diff --git a/_static/locales/pt/LC_MESSAGES/booktheme.po b/_static/locales/pt/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..50932b41df --- /dev/null +++ b/_static/locales/pt/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Baixar arquivo de notebook" + +msgid "Fullscreen mode" +msgstr "Modo tela cheia" + +msgid "Source repository" +msgstr "Repositório fonte" + +msgid "Print to PDF" +msgstr "Imprimir em PDF" + +msgid "Copyright" +msgstr "direito autoral" + +msgid "Sphinx Book Theme" +msgstr "Tema do livro Sphinx" + +msgid "By the" +msgstr "Pelo" + +msgid "Download this page" +msgstr "Baixe esta página" + +msgid "repository" +msgstr "repositório" + +msgid "Download source file" +msgstr "Baixar arquivo fonte" + +msgid "By" +msgstr "De" + +msgid "Toggle navigation" +msgstr "Alternar de navegação" + +msgid "next page" +msgstr "próxima página" + +msgid "Last updated on" +msgstr "Última atualização em" + +msgid "Contents" +msgstr "Conteúdo" + +msgid "Launch" +msgstr "Lançamento" + +msgid "Theme by the" +msgstr "Tema por" + +msgid "Open an issue" +msgstr "Abra um problema" + +msgid "open issue" +msgstr "questão aberta" + +msgid "suggest edit" +msgstr "sugerir edição" + +msgid "Edit this page" +msgstr "Edite essa página" + +msgid "previous page" +msgstr "página anterior" diff --git a/_static/locales/ro/LC_MESSAGES/booktheme.po b/_static/locales/ro/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..4a155fd175 --- /dev/null +++ b/_static/locales/ro/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ro\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Descărcați fișierul notebook" + +msgid "Fullscreen mode" +msgstr "Modul ecran întreg" + +msgid "Source repository" +msgstr "Depozit sursă" + +msgid "Print to PDF" +msgstr "Imprimați în PDF" + +msgid "Copyright" +msgstr "Drepturi de autor" + +msgid "Sphinx Book Theme" +msgstr "Tema Sphinx Book" + +msgid "By the" +msgstr "Langa" + +msgid "Download this page" +msgstr "Descarcă această pagină" + +msgid "repository" +msgstr "repertoriu" + +msgid "Download source file" +msgstr "Descărcați fișierul sursă" + +msgid "By" +msgstr "De" + +msgid "Toggle navigation" +msgstr "Comutare navigare" + +msgid "next page" +msgstr "pagina următoare" + +msgid "Last updated on" +msgstr "Ultima actualizare la" + +msgid "Contents" +msgstr "Cuprins" + +msgid "Launch" +msgstr "Lansa" + +msgid "Theme by the" +msgstr "Tema de" + +msgid "Open an issue" +msgstr "Deschideți o problemă" + +msgid "open issue" +msgstr "problema deschisă" + +msgid "suggest edit" +msgstr "sugerează editare" + +msgid "Edit this page" +msgstr "Editați această pagină" + +msgid "previous page" +msgstr "pagina anterioară" diff --git a/_static/locales/ru/LC_MESSAGES/booktheme.po b/_static/locales/ru/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..1e53c0362c --- /dev/null +++ b/_static/locales/ru/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Скачать файл записной книжки" + +msgid "Fullscreen mode" +msgstr "Полноэкранный режим" + +msgid "Source repository" +msgstr "Исходный репозиторий" + +msgid "Print to PDF" +msgstr "Распечатать в PDF" + +msgid "Copyright" +msgstr "авторское право" + +msgid "Sphinx Book Theme" +msgstr "Тема книги Сфинкс" + +msgid "By the" +msgstr "Посредством" + +msgid "Download this page" +msgstr "Загрузите эту страницу" + +msgid "repository" +msgstr "хранилище" + +msgid "Download source file" +msgstr "Скачать исходный файл" + +msgid "By" +msgstr "Автор:" + +msgid "Toggle navigation" +msgstr "Переключить навигацию" + +msgid "next page" +msgstr "Следующая страница" + +msgid "Last updated on" +msgstr "Последнее обновление" + +msgid "Contents" +msgstr "Содержание" + +msgid "Launch" +msgstr "Запуск" + +msgid "Theme by the" +msgstr "Тема от" + +msgid "Open an issue" +msgstr "Открыть вопрос" + +msgid "open issue" +msgstr "открытый вопрос" + +msgid "suggest edit" +msgstr "предложить редактировать" + +msgid "Edit this page" +msgstr "Редактировать эту страницу" + +msgid "previous page" +msgstr "Предыдущая страница" diff --git a/_static/locales/sk/LC_MESSAGES/booktheme.po b/_static/locales/sk/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..c544feac27 --- /dev/null +++ b/_static/locales/sk/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sk\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Stiahnite si zošit" + +msgid "Fullscreen mode" +msgstr "Režim celej obrazovky" + +msgid "Source repository" +msgstr "Zdrojové úložisko" + +msgid "Print to PDF" +msgstr "Tlač do PDF" + +msgid "Copyright" +msgstr "Autorské práva" + +msgid "Sphinx Book Theme" +msgstr "Téma knihy Sfinga" + +msgid "By the" +msgstr "Podľa" + +msgid "Download this page" +msgstr "Stiahnite si túto stránku" + +msgid "repository" +msgstr "Úložisko" + +msgid "Download source file" +msgstr "Stiahnite si zdrojový súbor" + +msgid "By" +msgstr "Autor:" + +msgid "Toggle navigation" +msgstr "Prepnúť navigáciu" + +msgid "next page" +msgstr "ďalšia strana" + +msgid "Last updated on" +msgstr "Posledná aktualizácia dňa" + +msgid "Contents" +msgstr "Obsah" + +msgid "Launch" +msgstr "Spustiť" + +msgid "Theme by the" +msgstr "Téma od" + +msgid "Open an issue" +msgstr "Otvorte problém" + +msgid "open issue" +msgstr "otvorené vydanie" + +msgid "suggest edit" +msgstr "navrhnúť úpravu" + +msgid "Edit this page" +msgstr "Upraviť túto stránku" + +msgid "previous page" +msgstr "predchádzajúca strana" diff --git a/_static/locales/sl/LC_MESSAGES/booktheme.po b/_static/locales/sl/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..f277a412eb --- /dev/null +++ b/_static/locales/sl/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Prenesite datoteko zvezka" + +msgid "Fullscreen mode" +msgstr "Celozaslonski način" + +msgid "Source repository" +msgstr "Izvorno skladišče" + +msgid "Print to PDF" +msgstr "Natisni v PDF" + +msgid "Copyright" +msgstr "avtorske pravice" + +msgid "Sphinx Book Theme" +msgstr "Tema knjige Sphinx" + +msgid "By the" +msgstr "Avtor" + +msgid "Download this page" +msgstr "Prenesite to stran" + +msgid "repository" +msgstr "odlagališče" + +msgid "Download source file" +msgstr "Prenesite izvorno datoteko" + +msgid "By" +msgstr "Avtor" + +msgid "Toggle navigation" +msgstr "Preklopi navigacijo" + +msgid "next page" +msgstr "Naslednja stran" + +msgid "Last updated on" +msgstr "Nazadnje posodobljeno dne" + +msgid "Contents" +msgstr "Vsebina" + +msgid "Launch" +msgstr "Kosilo" + +msgid "Theme by the" +msgstr "Tema avtorja" + +msgid "Open an issue" +msgstr "Odprite številko" + +msgid "open issue" +msgstr "odprto vprašanje" + +msgid "suggest edit" +msgstr "predlagajte urejanje" + +msgid "Edit this page" +msgstr "Uredite to stran" + +msgid "previous page" +msgstr "Prejšnja stran" diff --git a/_static/locales/sr/LC_MESSAGES/booktheme.po b/_static/locales/sr/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..60f407e996 --- /dev/null +++ b/_static/locales/sr/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sr\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Преузмите датотеку бележнице" + +msgid "Fullscreen mode" +msgstr "Режим целог екрана" + +msgid "Source repository" +msgstr "Изворно спремиште" + +msgid "Print to PDF" +msgstr "Испис у ПДФ" + +msgid "Copyright" +msgstr "Ауторско право" + +msgid "Sphinx Book Theme" +msgstr "Тема књиге Спхинк" + +msgid "By the" +msgstr "Од" + +msgid "Download this page" +msgstr "Преузмите ову страницу" + +msgid "repository" +msgstr "спремиште" + +msgid "Download source file" +msgstr "Преузми изворну датотеку" + +msgid "By" +msgstr "Од стране" + +msgid "Toggle navigation" +msgstr "Укључи / искључи навигацију" + +msgid "next page" +msgstr "Следећа страна" + +msgid "Last updated on" +msgstr "Последње ажурирање" + +msgid "Contents" +msgstr "Садржај" + +msgid "Launch" +msgstr "Лансирање" + +msgid "Theme by the" +msgstr "Тхеме би" + +msgid "Open an issue" +msgstr "Отворите издање" + +msgid "open issue" +msgstr "отворено издање" + +msgid "suggest edit" +msgstr "предложи уређивање" + +msgid "Edit this page" +msgstr "Уредите ову страницу" + +msgid "previous page" +msgstr "Претходна страница" diff --git a/_static/locales/sv/LC_MESSAGES/booktheme.po b/_static/locales/sv/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..a6090e3e72 --- /dev/null +++ b/_static/locales/sv/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sv\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Ladda ner notebook-fil" + +msgid "Fullscreen mode" +msgstr "Fullskärmsläge" + +msgid "Source repository" +msgstr "Källkodsrepositorium" + +msgid "Print to PDF" +msgstr "Skriv ut till PDF" + +msgid "Copyright" +msgstr "Upphovsrätt" + +msgid "Sphinx Book Theme" +msgstr "Sphinx Boktema" + +msgid "By the" +msgstr "Av den" + +msgid "Download this page" +msgstr "Ladda ner den här sidan" + +msgid "repository" +msgstr "repositorium" + +msgid "Download source file" +msgstr "Ladda ner källfil" + +msgid "By" +msgstr "Av" + +msgid "Toggle navigation" +msgstr "Växla navigering" + +msgid "next page" +msgstr "nästa sida" + +msgid "Last updated on" +msgstr "Senast uppdaterad den" + +msgid "Contents" +msgstr "Innehåll" + +msgid "Launch" +msgstr "Öppna" + +msgid "Theme by the" +msgstr "Tema av" + +msgid "Open an issue" +msgstr "Öppna en problemrapport" + +msgid "open issue" +msgstr "öppna problemrapport" + +msgid "suggest edit" +msgstr "föreslå ändring" + +msgid "Edit this page" +msgstr "Redigera den här sidan" + +msgid "previous page" +msgstr "föregående sida" diff --git a/_static/locales/ta/LC_MESSAGES/booktheme.po b/_static/locales/ta/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..93eee27d80 --- /dev/null +++ b/_static/locales/ta/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ta\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "நோட்புக் கோப்பைப் பதிவிறக்கவும்" + +msgid "Source repository" +msgstr "மூல களஞ்சியம்" + +msgid "Print to PDF" +msgstr "PDF இல் அச்சிடுக" + +msgid "Copyright" +msgstr "பதிப்புரிமை" + +msgid "Sphinx Book Theme" +msgstr "ஸ்பிங்க்ஸ் புத்தக தீம்" + +msgid "By the" +msgstr "மூலம்" + +msgid "Download this page" +msgstr "இந்தப் பக்கத்தைப் பதிவிறக்கவும்" + +msgid "Download source file" +msgstr "மூல கோப்பைப் பதிவிறக்குக" + +msgid "By" +msgstr "வழங்கியவர்" + +msgid "Toggle navigation" +msgstr "வழிசெலுத்தலை நிலைமாற்று" + +msgid "next page" +msgstr "அடுத்த பக்கம்" + +msgid "Last updated on" +msgstr "கடைசியாக புதுப்பிக்கப்பட்டது" + +msgid "Launch" +msgstr "தொடங்க" + +msgid "Theme by the" +msgstr "வழங்கிய தீம்" + +msgid "Open an issue" +msgstr "சிக்கலைத் திறக்கவும்" + +msgid "open issue" +msgstr "திறந்த பிரச்சினை" + +msgid "suggest edit" +msgstr "திருத்த பரிந்துரைக்கவும்" + +msgid "Edit this page" +msgstr "இந்தப் பக்கத்தைத் திருத்தவும்" + +msgid "previous page" +msgstr "முந்தைய பக்கம்" diff --git a/_static/locales/te/LC_MESSAGES/booktheme.po b/_static/locales/te/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..f60892cfda --- /dev/null +++ b/_static/locales/te/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: te\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "నోట్బుక్ ఫైల్ను డౌన్లోడ్ చేయండి" + +msgid "Source repository" +msgstr "మూల రిపోజిటరీ" + +msgid "Print to PDF" +msgstr "PDF కి ముద్రించండి" + +msgid "Copyright" +msgstr "కాపీరైట్" + +msgid "Sphinx Book Theme" +msgstr "సింహిక పుస్తక థీమ్" + +msgid "By the" +msgstr "ద్వారా" + +msgid "Download this page" +msgstr "ఈ పేజీని డౌన్‌లోడ్ చేయండి" + +msgid "Download source file" +msgstr "మూల ఫైల్‌ను డౌన్‌లోడ్ చేయండి" + +msgid "By" +msgstr "ద్వారా" + +msgid "Toggle navigation" +msgstr "నావిగేషన్‌ను టోగుల్ చేయండి" + +msgid "next page" +msgstr "తరువాతి పేజీ" + +msgid "Last updated on" +msgstr "చివరిగా నవీకరించబడింది" + +msgid "Launch" +msgstr "ప్రారంభించండి" + +msgid "Theme by the" +msgstr "ద్వారా థీమ్" + +msgid "Open an issue" +msgstr "సమస్యను తెరవండి" + +msgid "open issue" +msgstr "ఓపెన్ ఇష్యూ" + +msgid "suggest edit" +msgstr "సవరించమని సూచించండి" + +msgid "Edit this page" +msgstr "ఈ పేజీని సవరించండి" + +msgid "previous page" +msgstr "ముందు పేజి" diff --git a/_static/locales/tg/LC_MESSAGES/booktheme.po b/_static/locales/tg/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..a9daf7d4a3 --- /dev/null +++ b/_static/locales/tg/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tg\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Файли дафтарро зеркашӣ кунед" + +msgid "Fullscreen mode" +msgstr "Ҳолати экрани пурра" + +msgid "Source repository" +msgstr "Анбори манбаъ" + +msgid "Print to PDF" +msgstr "Чоп ба PDF" + +msgid "Copyright" +msgstr "Ҳуқуқи муаллиф" + +msgid "Sphinx Book Theme" +msgstr "Сфинкс Мавзӯи китоб" + +msgid "By the" +msgstr "Бо" + +msgid "Download this page" +msgstr "Ин саҳифаро зеркашӣ кунед" + +msgid "repository" +msgstr "анбор" + +msgid "Download source file" +msgstr "Файли манбаъро зеркашӣ кунед" + +msgid "By" +msgstr "Бо" + +msgid "Toggle navigation" +msgstr "Гузаришро иваз кунед" + +msgid "next page" +msgstr "саҳифаи оянда" + +msgid "Last updated on" +msgstr "Last навсозӣ дар" + +msgid "Contents" +msgstr "Мундариҷа" + +msgid "Launch" +msgstr "Оғоз" + +msgid "Theme by the" +msgstr "Мавзӯъи аз" + +msgid "Open an issue" +msgstr "Масъаларо кушоед" + +msgid "open issue" +msgstr "барориши кушод" + +msgid "suggest edit" +msgstr "пешниҳод вироиш" + +msgid "Edit this page" +msgstr "Ин саҳифаро таҳрир кунед" + +msgid "previous page" +msgstr "саҳифаи қаблӣ" diff --git a/_static/locales/th/LC_MESSAGES/booktheme.po b/_static/locales/th/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..2a5764930b --- /dev/null +++ b/_static/locales/th/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: th\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "ดาวน์โหลดไฟล์สมุดบันทึก" + +msgid "Fullscreen mode" +msgstr "โหมดเต็มหน้าจอ" + +msgid "Source repository" +msgstr "ที่เก็บซอร์ส" + +msgid "Print to PDF" +msgstr "พิมพ์เป็น PDF" + +msgid "Copyright" +msgstr "ลิขสิทธิ์" + +msgid "Sphinx Book Theme" +msgstr "ธีมหนังสือสฟิงซ์" + +msgid "By the" +msgstr "โดย" + +msgid "Download this page" +msgstr "ดาวน์โหลดหน้านี้" + +msgid "repository" +msgstr "ที่เก็บ" + +msgid "Download source file" +msgstr "ดาวน์โหลดไฟล์ต้นฉบับ" + +msgid "By" +msgstr "โดย" + +msgid "Toggle navigation" +msgstr "ไม่ต้องสลับช่องทาง" + +msgid "next page" +msgstr "หน้าต่อไป" + +msgid "Last updated on" +msgstr "ปรับปรุงล่าสุดเมื่อ" + +msgid "Contents" +msgstr "สารบัญ" + +msgid "Launch" +msgstr "เปิด" + +msgid "Theme by the" +msgstr "ธีมโดย" + +msgid "Open an issue" +msgstr "เปิดปัญหา" + +msgid "open issue" +msgstr "เปิดปัญหา" + +msgid "suggest edit" +msgstr "แนะนำแก้ไข" + +msgid "Edit this page" +msgstr "แก้ไขหน้านี้" + +msgid "previous page" +msgstr "หน้าที่แล้ว" diff --git a/_static/locales/tl/LC_MESSAGES/booktheme.po b/_static/locales/tl/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..bb548de9e9 --- /dev/null +++ b/_static/locales/tl/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Mag-download ng file ng notebook" + +msgid "Source repository" +msgstr "Pinagmulan ng imbakan" + +msgid "Print to PDF" +msgstr "I-print sa PDF" + +msgid "Copyright" +msgstr "Copyright" + +msgid "Sphinx Book Theme" +msgstr "Tema ng Sphinx Book" + +msgid "By the" +msgstr "Sa pamamagitan ng" + +msgid "Download this page" +msgstr "I-download ang pahinang ito" + +msgid "Download source file" +msgstr "Mag-download ng file ng pinagmulan" + +msgid "By" +msgstr "Ni" + +msgid "Toggle navigation" +msgstr "I-toggle ang pag-navigate" + +msgid "next page" +msgstr "Susunod na pahina" + +msgid "Last updated on" +msgstr "Huling na-update noong" + +msgid "Launch" +msgstr "Ilunsad" + +msgid "Theme by the" +msgstr "Tema ng" + +msgid "Open an issue" +msgstr "Magbukas ng isyu" + +msgid "open issue" +msgstr "bukas na isyu" + +msgid "suggest edit" +msgstr "iminumungkahi i-edit" + +msgid "Edit this page" +msgstr "I-edit ang pahinang ito" + +msgid "previous page" +msgstr "Nakaraang pahina" diff --git a/_static/locales/tr/LC_MESSAGES/booktheme.po b/_static/locales/tr/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..4f77a8898a --- /dev/null +++ b/_static/locales/tr/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Defter dosyasını indirin" + +msgid "Fullscreen mode" +msgstr "Tam ekran modu" + +msgid "Source repository" +msgstr "Kaynak kod deposu" + +msgid "Print to PDF" +msgstr "PDF olarak yazdır" + +msgid "Copyright" +msgstr "Telif hakkı" + +msgid "Sphinx Book Theme" +msgstr "Sfenks Kitap Teması" + +msgid "By the" +msgstr "Tarafından" + +msgid "Download this page" +msgstr "Bu sayfayı indirin" + +msgid "repository" +msgstr "depo" + +msgid "Download source file" +msgstr "Kaynak dosyayı indirin" + +msgid "By" +msgstr "Tarafından" + +msgid "Toggle navigation" +msgstr "Gezinmeyi değiştir" + +msgid "next page" +msgstr "sonraki Sayfa" + +msgid "Last updated on" +msgstr "Son güncelleme tarihi" + +msgid "Contents" +msgstr "İçindekiler" + +msgid "Launch" +msgstr "Başlatmak" + +msgid "Theme by the" +msgstr "Tarafından tema" + +msgid "Open an issue" +msgstr "Bir sorunu açın" + +msgid "open issue" +msgstr "Açık konu" + +msgid "suggest edit" +msgstr "düzenleme öner" + +msgid "Edit this page" +msgstr "Bu sayfayı düzenle" + +msgid "previous page" +msgstr "önceki sayfa" diff --git a/_static/locales/uk/LC_MESSAGES/booktheme.po b/_static/locales/uk/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..6cf45508e7 --- /dev/null +++ b/_static/locales/uk/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: uk\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Завантажте файл блокнота" + +msgid "Fullscreen mode" +msgstr "Повноекранний режим" + +msgid "Source repository" +msgstr "Джерело сховища" + +msgid "Print to PDF" +msgstr "Друк у форматі PDF" + +msgid "Copyright" +msgstr "Авторське право" + +msgid "Sphinx Book Theme" +msgstr "Тема книги \"Сфінкс\"" + +msgid "By the" +msgstr "По" + +msgid "Download this page" +msgstr "Завантажте цю сторінку" + +msgid "repository" +msgstr "сховище" + +msgid "Download source file" +msgstr "Завантажити вихідний файл" + +msgid "By" +msgstr "Автор" + +msgid "Toggle navigation" +msgstr "Переключити навігацію" + +msgid "next page" +msgstr "Наступна сторінка" + +msgid "Last updated on" +msgstr "Останнє оновлення:" + +msgid "Contents" +msgstr "Зміст" + +msgid "Launch" +msgstr "Запуск" + +msgid "Theme by the" +msgstr "Тема від" + +msgid "Open an issue" +msgstr "Відкрийте випуск" + +msgid "open issue" +msgstr "відкритий випуск" + +msgid "suggest edit" +msgstr "запропонувати редагувати" + +msgid "Edit this page" +msgstr "Редагувати цю сторінку" + +msgid "previous page" +msgstr "Попередня сторінка" diff --git a/_static/locales/ur/LC_MESSAGES/booktheme.po b/_static/locales/ur/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..7d5738f400 --- /dev/null +++ b/_static/locales/ur/LC_MESSAGES/booktheme.po @@ -0,0 +1,66 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ur\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "نوٹ بک فائل ڈاؤن لوڈ کریں" + +msgid "Source repository" +msgstr "ماخذ ذخیرہ" + +msgid "Print to PDF" +msgstr "پی ڈی ایف پرنٹ کریں" + +msgid "Copyright" +msgstr "کاپی رائٹ" + +msgid "Sphinx Book Theme" +msgstr "سپنکس بک تھیم" + +msgid "By the" +msgstr "کی طرف" + +msgid "Download this page" +msgstr "اس صفحے کو ڈاؤن لوڈ کریں" + +msgid "Download source file" +msgstr "سورس فائل ڈاؤن لوڈ کریں" + +msgid "By" +msgstr "بذریعہ" + +msgid "Toggle navigation" +msgstr "نیویگیشن ٹوگل کریں" + +msgid "next page" +msgstr "اگلا صفحہ" + +msgid "Last updated on" +msgstr "آخری بار تازہ کاری ہوئی" + +msgid "Launch" +msgstr "لانچ کریں" + +msgid "Theme by the" +msgstr "کے ذریعہ تھیم" + +msgid "Open an issue" +msgstr "ایک مسئلہ کھولیں" + +msgid "open issue" +msgstr "کھلا مسئلہ" + +msgid "suggest edit" +msgstr "ترمیم کی تجویز کریں" + +msgid "Edit this page" +msgstr "اس صفحے میں ترمیم کریں" + +msgid "previous page" +msgstr "سابقہ ​​صفحہ" diff --git a/_static/locales/vi/LC_MESSAGES/booktheme.po b/_static/locales/vi/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..e6c090a4e4 --- /dev/null +++ b/_static/locales/vi/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: vi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "Tải xuống tệp sổ tay" + +msgid "Fullscreen mode" +msgstr "Chế độ toàn màn hình" + +msgid "Source repository" +msgstr "Kho nguồn" + +msgid "Print to PDF" +msgstr "In sang PDF" + +msgid "Copyright" +msgstr "Bản quyền" + +msgid "Sphinx Book Theme" +msgstr "Chủ đề sách nhân sư" + +msgid "By the" +msgstr "Bằng" + +msgid "Download this page" +msgstr "Tải xuống trang này" + +msgid "repository" +msgstr "kho" + +msgid "Download source file" +msgstr "Tải xuống tệp nguồn" + +msgid "By" +msgstr "Bởi" + +msgid "Toggle navigation" +msgstr "Chuyển đổi điều hướng thành" + +msgid "next page" +msgstr "Trang tiếp theo" + +msgid "Last updated on" +msgstr "Cập nhật lần cuối vào" + +msgid "Contents" +msgstr "Nội dung" + +msgid "Launch" +msgstr "Phóng" + +msgid "Theme by the" +msgstr "Chủ đề của" + +msgid "Open an issue" +msgstr "Mở một vấn đề" + +msgid "open issue" +msgstr "vấn đề mở" + +msgid "suggest edit" +msgstr "đề nghị chỉnh sửa" + +msgid "Edit this page" +msgstr "chỉnh sửa trang này" + +msgid "previous page" +msgstr "trang trước" diff --git a/_static/locales/zh_CN/LC_MESSAGES/booktheme.po b/_static/locales/zh_CN/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..93ddbd3556 --- /dev/null +++ b/_static/locales/zh_CN/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "下载笔记本文件" + +msgid "Fullscreen mode" +msgstr "全屏模式" + +msgid "Source repository" +msgstr "源码库" + +msgid "Print to PDF" +msgstr "列印成 PDF" + +msgid "Copyright" +msgstr "版权" + +msgid "Sphinx Book Theme" +msgstr "Sphinx Book 主题" + +msgid "By the" +msgstr "作者:" + +msgid "Download this page" +msgstr "下载此页面" + +msgid "repository" +msgstr "仓库" + +msgid "Download source file" +msgstr "下载源文件" + +msgid "By" +msgstr "作者:" + +msgid "Toggle navigation" +msgstr "显示或隐藏导航栏" + +msgid "next page" +msgstr "下一页" + +msgid "Last updated on" +msgstr "上次更新时间:" + +msgid "Contents" +msgstr "目录" + +msgid "Launch" +msgstr "启动" + +msgid "Theme by the" +msgstr "主题作者:" + +msgid "Open an issue" +msgstr "创建议题" + +msgid "open issue" +msgstr "创建议题" + +msgid "suggest edit" +msgstr "提出修改建议" + +msgid "Edit this page" +msgstr "编辑此页面" + +msgid "previous page" +msgstr "上一页" diff --git a/_static/locales/zh_TW/LC_MESSAGES/booktheme.po b/_static/locales/zh_TW/LC_MESSAGES/booktheme.po new file mode 100644 index 0000000000..dc7537b391 --- /dev/null +++ b/_static/locales/zh_TW/LC_MESSAGES/booktheme.po @@ -0,0 +1,75 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Sphinx-Book-Theme\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_TW\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Download notebook file" +msgstr "下載 Notebook 檔案" + +msgid "Fullscreen mode" +msgstr "全螢幕模式" + +msgid "Source repository" +msgstr "來源儲存庫" + +msgid "Print to PDF" +msgstr "列印成 PDF" + +msgid "Copyright" +msgstr "Copyright" + +msgid "Sphinx Book Theme" +msgstr "Sphinx Book 佈景主題" + +msgid "By the" +msgstr "作者:" + +msgid "Download this page" +msgstr "下載此頁面" + +msgid "repository" +msgstr "儲存庫" + +msgid "Download source file" +msgstr "下載原始檔" + +msgid "By" +msgstr "作者:" + +msgid "Toggle navigation" +msgstr "顯示或隱藏導覽列" + +msgid "next page" +msgstr "下一頁" + +msgid "Last updated on" +msgstr "最後更新時間:" + +msgid "Contents" +msgstr "目錄" + +msgid "Launch" +msgstr "啟動" + +msgid "Theme by the" +msgstr "佈景主題作者:" + +msgid "Open an issue" +msgstr "開啟議題" + +msgid "open issue" +msgstr "公開的問題" + +msgid "suggest edit" +msgstr "提出修改建議" + +msgid "Edit this page" +msgstr "編輯此頁面" + +msgid "previous page" +msgstr "上一頁" diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000000..d96755fdaf Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/oneAPI-rgb-rev-100.png b/_static/oneAPI-rgb-rev-100.png new file mode 100644 index 0000000000..58d2d5c54e Binary files /dev/null and b/_static/oneAPI-rgb-rev-100.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000000..7107cec93a Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000000..012e6a00a4 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,152 @@ +html[data-theme="light"] .highlight pre { line-height: 125%; } +html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight .hll { background-color: #fae4c2 } +html[data-theme="light"] .highlight { background: #fefefe; color: #080808 } +html[data-theme="light"] .highlight .c { color: #515151 } /* Comment */ +html[data-theme="light"] .highlight .err { color: #a12236 } /* Error */ +html[data-theme="light"] .highlight .k { color: #6730c5 } /* Keyword */ +html[data-theme="light"] .highlight .l { color: #7f4707 } /* Literal */ +html[data-theme="light"] .highlight .n { color: #080808 } /* Name */ +html[data-theme="light"] .highlight .o { color: #00622f } /* Operator */ +html[data-theme="light"] .highlight .p { color: #080808 } /* Punctuation */ +html[data-theme="light"] .highlight .ch { color: #515151 } /* Comment.Hashbang */ +html[data-theme="light"] .highlight .cm { color: #515151 } /* Comment.Multiline */ +html[data-theme="light"] .highlight .cp { color: #515151 } /* Comment.Preproc */ +html[data-theme="light"] .highlight .cpf { color: #515151 } /* Comment.PreprocFile */ +html[data-theme="light"] .highlight .c1 { color: #515151 } /* Comment.Single */ +html[data-theme="light"] .highlight .cs { color: #515151 } /* Comment.Special */ +html[data-theme="light"] .highlight .gd { color: #005b82 } /* Generic.Deleted */ +html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="light"] .highlight .gh { color: #005b82 } /* Generic.Heading */ +html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="light"] .highlight .gu { color: #005b82 } /* Generic.Subheading */ +html[data-theme="light"] .highlight .kc { color: #6730c5 } /* Keyword.Constant */ +html[data-theme="light"] .highlight .kd { color: #6730c5 } /* Keyword.Declaration */ +html[data-theme="light"] .highlight .kn { color: #6730c5 } /* Keyword.Namespace */ +html[data-theme="light"] .highlight .kp { color: #6730c5 } /* Keyword.Pseudo */ +html[data-theme="light"] .highlight .kr { color: #6730c5 } /* Keyword.Reserved */ +html[data-theme="light"] .highlight .kt { color: #7f4707 } /* Keyword.Type */ +html[data-theme="light"] .highlight .ld { color: #7f4707 } /* Literal.Date */ +html[data-theme="light"] .highlight .m { color: #7f4707 } /* Literal.Number */ +html[data-theme="light"] .highlight .s { color: #00622f } /* Literal.String */ +html[data-theme="light"] .highlight .na { color: #912583 } /* Name.Attribute */ +html[data-theme="light"] .highlight .nb { color: #7f4707 } /* Name.Builtin */ +html[data-theme="light"] .highlight .nc { color: #005b82 } /* Name.Class */ +html[data-theme="light"] .highlight .no { color: #005b82 } /* Name.Constant */ +html[data-theme="light"] .highlight .nd { color: #7f4707 } /* Name.Decorator */ +html[data-theme="light"] .highlight .ni { color: #00622f } /* Name.Entity */ +html[data-theme="light"] .highlight .ne { color: #6730c5 } /* Name.Exception */ +html[data-theme="light"] .highlight .nf { color: #005b82 } /* Name.Function */ +html[data-theme="light"] .highlight .nl { color: #7f4707 } /* Name.Label */ +html[data-theme="light"] .highlight .nn { color: #080808 } /* Name.Namespace */ +html[data-theme="light"] .highlight .nx { color: #080808 } /* Name.Other */ +html[data-theme="light"] .highlight .py { color: #005b82 } /* Name.Property */ +html[data-theme="light"] .highlight .nt { color: #005b82 } /* Name.Tag */ +html[data-theme="light"] .highlight .nv { color: #a12236 } /* Name.Variable */ +html[data-theme="light"] .highlight .ow { color: #6730c5 } /* Operator.Word */ +html[data-theme="light"] .highlight .pm { color: #080808 } /* Punctuation.Marker */ +html[data-theme="light"] .highlight .w { color: #080808 } /* Text.Whitespace */ +html[data-theme="light"] .highlight .mb { color: #7f4707 } /* Literal.Number.Bin */ +html[data-theme="light"] .highlight .mf { color: #7f4707 } /* Literal.Number.Float */ +html[data-theme="light"] .highlight .mh { color: #7f4707 } /* Literal.Number.Hex */ +html[data-theme="light"] .highlight .mi { color: #7f4707 } /* Literal.Number.Integer */ +html[data-theme="light"] .highlight .mo { color: #7f4707 } /* Literal.Number.Oct */ +html[data-theme="light"] .highlight .sa { color: #00622f } /* Literal.String.Affix */ +html[data-theme="light"] .highlight .sb { color: #00622f } /* Literal.String.Backtick */ +html[data-theme="light"] .highlight .sc { color: #00622f } /* Literal.String.Char */ +html[data-theme="light"] .highlight .dl { color: #00622f } /* Literal.String.Delimiter */ +html[data-theme="light"] .highlight .sd { color: #00622f } /* Literal.String.Doc */ +html[data-theme="light"] .highlight .s2 { color: #00622f } /* Literal.String.Double */ +html[data-theme="light"] .highlight .se { color: #00622f } /* Literal.String.Escape */ +html[data-theme="light"] .highlight .sh { color: #00622f } /* Literal.String.Heredoc */ +html[data-theme="light"] .highlight .si { color: #00622f } /* Literal.String.Interpol */ +html[data-theme="light"] .highlight .sx { color: #00622f } /* Literal.String.Other */ +html[data-theme="light"] .highlight .sr { color: #a12236 } /* Literal.String.Regex */ +html[data-theme="light"] .highlight .s1 { color: #00622f } /* Literal.String.Single */ +html[data-theme="light"] .highlight .ss { color: #005b82 } /* Literal.String.Symbol */ +html[data-theme="light"] .highlight .bp { color: #7f4707 } /* Name.Builtin.Pseudo */ +html[data-theme="light"] .highlight .fm { color: #005b82 } /* Name.Function.Magic */ +html[data-theme="light"] .highlight .vc { color: #a12236 } /* Name.Variable.Class */ +html[data-theme="light"] .highlight .vg { color: #a12236 } /* Name.Variable.Global */ +html[data-theme="light"] .highlight .vi { color: #a12236 } /* Name.Variable.Instance */ +html[data-theme="light"] .highlight .vm { color: #7f4707 } /* Name.Variable.Magic */ +html[data-theme="light"] .highlight .il { color: #7f4707 } /* Literal.Number.Integer.Long */ +html[data-theme="dark"] .highlight pre { line-height: 125%; } +html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e } +html[data-theme="dark"] .highlight { background: #2b2b2b; color: #f8f8f2 } +html[data-theme="dark"] .highlight .c { color: #ffd900 } /* Comment */ +html[data-theme="dark"] .highlight .err { color: #ffa07a } /* Error */ +html[data-theme="dark"] .highlight .k { color: #dcc6e0 } /* Keyword */ +html[data-theme="dark"] .highlight .l { color: #ffd900 } /* Literal */ +html[data-theme="dark"] .highlight .n { color: #f8f8f2 } /* Name */ +html[data-theme="dark"] .highlight .o { color: #abe338 } /* Operator */ +html[data-theme="dark"] .highlight .p { color: #f8f8f2 } /* Punctuation */ +html[data-theme="dark"] .highlight .ch { color: #ffd900 } /* Comment.Hashbang */ +html[data-theme="dark"] .highlight .cm { color: #ffd900 } /* Comment.Multiline */ +html[data-theme="dark"] .highlight .cp { color: #ffd900 } /* Comment.Preproc */ +html[data-theme="dark"] .highlight .cpf { color: #ffd900 } /* Comment.PreprocFile */ +html[data-theme="dark"] .highlight .c1 { color: #ffd900 } /* Comment.Single */ +html[data-theme="dark"] .highlight .cs { color: #ffd900 } /* Comment.Special */ +html[data-theme="dark"] .highlight .gd { color: #00e0e0 } /* Generic.Deleted */ +html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="dark"] .highlight .gh { color: #00e0e0 } /* Generic.Heading */ +html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="dark"] .highlight .gu { color: #00e0e0 } /* Generic.Subheading */ +html[data-theme="dark"] .highlight .kc { color: #dcc6e0 } /* Keyword.Constant */ +html[data-theme="dark"] .highlight .kd { color: #dcc6e0 } /* Keyword.Declaration */ +html[data-theme="dark"] .highlight .kn { color: #dcc6e0 } /* Keyword.Namespace */ +html[data-theme="dark"] .highlight .kp { color: #dcc6e0 } /* Keyword.Pseudo */ +html[data-theme="dark"] .highlight .kr { color: #dcc6e0 } /* Keyword.Reserved */ +html[data-theme="dark"] .highlight .kt { color: #ffd900 } /* Keyword.Type */ +html[data-theme="dark"] .highlight .ld { color: #ffd900 } /* Literal.Date */ +html[data-theme="dark"] .highlight .m { color: #ffd900 } /* Literal.Number */ +html[data-theme="dark"] .highlight .s { color: #abe338 } /* Literal.String */ +html[data-theme="dark"] .highlight .na { color: #ffd900 } /* Name.Attribute */ +html[data-theme="dark"] .highlight .nb { color: #ffd900 } /* Name.Builtin */ +html[data-theme="dark"] .highlight .nc { color: #00e0e0 } /* Name.Class */ +html[data-theme="dark"] .highlight .no { color: #00e0e0 } /* Name.Constant */ +html[data-theme="dark"] .highlight .nd { color: #ffd900 } /* Name.Decorator */ +html[data-theme="dark"] .highlight .ni { color: #abe338 } /* Name.Entity */ +html[data-theme="dark"] .highlight .ne { color: #dcc6e0 } /* Name.Exception */ +html[data-theme="dark"] .highlight .nf { color: #00e0e0 } /* Name.Function */ +html[data-theme="dark"] .highlight .nl { color: #ffd900 } /* Name.Label */ +html[data-theme="dark"] .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +html[data-theme="dark"] .highlight .nx { color: #f8f8f2 } /* Name.Other */ +html[data-theme="dark"] .highlight .py { color: #00e0e0 } /* Name.Property */ +html[data-theme="dark"] .highlight .nt { color: #00e0e0 } /* Name.Tag */ +html[data-theme="dark"] .highlight .nv { color: #ffa07a } /* Name.Variable */ +html[data-theme="dark"] .highlight .ow { color: #dcc6e0 } /* Operator.Word */ +html[data-theme="dark"] .highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */ +html[data-theme="dark"] .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +html[data-theme="dark"] .highlight .mb { color: #ffd900 } /* Literal.Number.Bin */ +html[data-theme="dark"] .highlight .mf { color: #ffd900 } /* Literal.Number.Float */ +html[data-theme="dark"] .highlight .mh { color: #ffd900 } /* Literal.Number.Hex */ +html[data-theme="dark"] .highlight .mi { color: #ffd900 } /* Literal.Number.Integer */ +html[data-theme="dark"] .highlight .mo { color: #ffd900 } /* Literal.Number.Oct */ +html[data-theme="dark"] .highlight .sa { color: #abe338 } /* Literal.String.Affix */ +html[data-theme="dark"] .highlight .sb { color: #abe338 } /* Literal.String.Backtick */ +html[data-theme="dark"] .highlight .sc { color: #abe338 } /* Literal.String.Char */ +html[data-theme="dark"] .highlight .dl { color: #abe338 } /* Literal.String.Delimiter */ +html[data-theme="dark"] .highlight .sd { color: #abe338 } /* Literal.String.Doc */ +html[data-theme="dark"] .highlight .s2 { color: #abe338 } /* Literal.String.Double */ +html[data-theme="dark"] .highlight .se { color: #abe338 } /* Literal.String.Escape */ +html[data-theme="dark"] .highlight .sh { color: #abe338 } /* Literal.String.Heredoc */ +html[data-theme="dark"] .highlight .si { color: #abe338 } /* Literal.String.Interpol */ +html[data-theme="dark"] .highlight .sx { color: #abe338 } /* Literal.String.Other */ +html[data-theme="dark"] .highlight .sr { color: #ffa07a } /* Literal.String.Regex */ +html[data-theme="dark"] .highlight .s1 { color: #abe338 } /* Literal.String.Single */ +html[data-theme="dark"] .highlight .ss { color: #00e0e0 } /* Literal.String.Symbol */ +html[data-theme="dark"] .highlight .bp { color: #ffd900 } /* Name.Builtin.Pseudo */ +html[data-theme="dark"] .highlight .fm { color: #00e0e0 } /* Name.Function.Magic */ +html[data-theme="dark"] .highlight .vc { color: #ffa07a } /* Name.Variable.Class */ +html[data-theme="dark"] .highlight .vg { color: #ffa07a } /* Name.Variable.Global */ +html[data-theme="dark"] .highlight .vi { color: #ffa07a } /* Name.Variable.Instance */ +html[data-theme="dark"] .highlight .vm { color: #ffd900 } /* Name.Variable.Magic */ +html[data-theme="dark"] .highlight .il { color: #ffd900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/sbt-webpack-macros.html b/_static/sbt-webpack-macros.html new file mode 100644 index 0000000000..6cbf559faa --- /dev/null +++ b/_static/sbt-webpack-macros.html @@ -0,0 +1,11 @@ + +{% macro head_pre_bootstrap() %} + +{% endmacro %} + +{% macro body_post() %} + +{% endmacro %} diff --git a/_static/scripts/bootstrap.js b/_static/scripts/bootstrap.js new file mode 100644 index 0000000000..c8178debbc --- /dev/null +++ b/_static/scripts/bootstrap.js @@ -0,0 +1,3 @@ +/*! For license information please see bootstrap.js.LICENSE.txt */ +(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>E,afterRead:()=>v,afterWrite:()=>C,applyStyles:()=>$,arrow:()=>J,auto:()=>a,basePlacements:()=>l,beforeMain:()=>y,beforeRead:()=>_,beforeWrite:()=>A,bottom:()=>s,clippingParents:()=>d,computeStyles:()=>it,createPopper:()=>Dt,createPopperBase:()=>St,createPopperLite:()=>$t,detectOverflow:()=>_t,end:()=>h,eventListeners:()=>st,flip:()=>bt,hide:()=>wt,left:()=>r,main:()=>w,modifierPhases:()=>O,offset:()=>Et,placements:()=>g,popper:()=>f,popperGenerator:()=>Lt,popperOffsets:()=>At,preventOverflow:()=>Tt,read:()=>b,reference:()=>p,right:()=>o,start:()=>c,top:()=>n,variationPlacements:()=>m,viewport:()=>u,write:()=>T});var i={};t.r(i),t.d(i,{Alert:()=>Oe,Button:()=>ke,Carousel:()=>li,Collapse:()=>Ei,Dropdown:()=>Ki,Modal:()=>Ln,Offcanvas:()=>Kn,Popover:()=>bs,ScrollSpy:()=>Ls,Tab:()=>Js,Toast:()=>po,Tooltip:()=>fs});var n="top",s="bottom",o="right",r="left",a="auto",l=[n,s,o,r],c="start",h="end",d="clippingParents",u="viewport",f="popper",p="reference",m=l.reduce((function(t,e){return t.concat([e+"-"+c,e+"-"+h])}),[]),g=[].concat(l,[a]).reduce((function(t,e){return t.concat([e,e+"-"+c,e+"-"+h])}),[]),_="beforeRead",b="read",v="afterRead",y="beforeMain",w="main",E="afterMain",A="beforeWrite",T="write",C="afterWrite",O=[_,b,v,y,w,E,A,T,C];function x(t){return t?(t.nodeName||"").toLowerCase():null}function k(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function L(t){return t instanceof k(t).Element||t instanceof Element}function S(t){return t instanceof k(t).HTMLElement||t instanceof HTMLElement}function D(t){return"undefined"!=typeof ShadowRoot&&(t instanceof k(t).ShadowRoot||t instanceof ShadowRoot)}const $={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];S(s)&&x(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});S(n)&&x(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function I(t){return t.split("-")[0]}var N=Math.max,P=Math.min,M=Math.round;function j(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function F(){return!/^((?!chrome|android).)*safari/i.test(j())}function H(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&S(t)&&(s=t.offsetWidth>0&&M(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&M(n.height)/t.offsetHeight||1);var r=(L(t)?k(t):window).visualViewport,a=!F()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function B(t){var e=H(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function W(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&D(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function z(t){return k(t).getComputedStyle(t)}function R(t){return["table","td","th"].indexOf(x(t))>=0}function q(t){return((L(t)?t.ownerDocument:t.document)||window.document).documentElement}function V(t){return"html"===x(t)?t:t.assignedSlot||t.parentNode||(D(t)?t.host:null)||q(t)}function Y(t){return S(t)&&"fixed"!==z(t).position?t.offsetParent:null}function K(t){for(var e=k(t),i=Y(t);i&&R(i)&&"static"===z(i).position;)i=Y(i);return i&&("html"===x(i)||"body"===x(i)&&"static"===z(i).position)?e:i||function(t){var e=/firefox/i.test(j());if(/Trident/i.test(j())&&S(t)&&"fixed"===z(t).position)return null;var i=V(t);for(D(i)&&(i=i.host);S(i)&&["html","body"].indexOf(x(i))<0;){var n=z(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Q(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function X(t,e,i){return N(t,P(e,i))}function U(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function G(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const J={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,a=t.name,c=t.options,h=i.elements.arrow,d=i.modifiersData.popperOffsets,u=I(i.placement),f=Q(u),p=[r,o].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return U("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:G(t,l))}(c.padding,i),g=B(h),_="y"===f?n:r,b="y"===f?s:o,v=i.rects.reference[p]+i.rects.reference[f]-d[f]-i.rects.popper[p],y=d[f]-i.rects.reference[f],w=K(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=X(T,O,C),k=f;i.modifiersData[a]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&W(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Z(t){return t.split("-")[1]}var tt={top:"auto",right:"auto",bottom:"auto",left:"auto"};function et(t){var e,i=t.popper,a=t.popperRect,l=t.placement,c=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=r,C=n,O=window;if(p){var x=K(i),L="clientHeight",S="clientWidth";x===k(i)&&"static"!==z(x=q(i)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===n||(l===r||l===o)&&c===h)&&(C=s,y-=(g&&x===O&&O.visualViewport?O.visualViewport.height:x[L])-a.height,y*=f?1:-1),l!==r&&(l!==n&&l!==s||c!==h)||(T=o,b-=(g&&x===O&&O.visualViewport?O.visualViewport.width:x[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&tt),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:M(i*s)/s||0,y:M(n*s)/s||0}}({x:b,y},k(i)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const it={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:I(e.placement),variation:Z(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,et(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,et(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var nt={passive:!0};const st={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=k(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,nt)})),a&&l.addEventListener("resize",i.update,nt),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,nt)})),a&&l.removeEventListener("resize",i.update,nt)}},data:{}};var ot={left:"right",right:"left",bottom:"top",top:"bottom"};function rt(t){return t.replace(/left|right|bottom|top/g,(function(t){return ot[t]}))}var at={start:"end",end:"start"};function lt(t){return t.replace(/start|end/g,(function(t){return at[t]}))}function ct(t){var e=k(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ht(t){return H(q(t)).left+ct(t).scrollLeft}function dt(t){var e=z(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function ut(t){return["html","body","#document"].indexOf(x(t))>=0?t.ownerDocument.body:S(t)&&dt(t)?t:ut(V(t))}function ft(t,e){var i;void 0===e&&(e=[]);var n=ut(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=k(n),r=s?[o].concat(o.visualViewport||[],dt(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ft(V(r)))}function pt(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function mt(t,e,i){return e===u?pt(function(t,e){var i=k(t),n=q(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=F();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ht(t),y:l}}(t,i)):L(e)?function(t,e){var i=H(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):pt(function(t){var e,i=q(t),n=ct(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=N(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=N(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ht(t),l=-n.scrollTop;return"rtl"===z(s||i).direction&&(a+=N(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(q(t)))}function gt(t){var e,i=t.reference,a=t.element,l=t.placement,d=l?I(l):null,u=l?Z(l):null,f=i.x+i.width/2-a.width/2,p=i.y+i.height/2-a.height/2;switch(d){case n:e={x:f,y:i.y-a.height};break;case s:e={x:f,y:i.y+i.height};break;case o:e={x:i.x+i.width,y:p};break;case r:e={x:i.x-a.width,y:p};break;default:e={x:i.x,y:i.y}}var m=d?Q(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case c:e[m]=e[m]-(i[g]/2-a[g]/2);break;case h:e[m]=e[m]+(i[g]/2-a[g]/2)}}return e}function _t(t,e){void 0===e&&(e={});var i=e,r=i.placement,a=void 0===r?t.placement:r,c=i.strategy,h=void 0===c?t.strategy:c,m=i.boundary,g=void 0===m?d:m,_=i.rootBoundary,b=void 0===_?u:_,v=i.elementContext,y=void 0===v?f:v,w=i.altBoundary,E=void 0!==w&&w,A=i.padding,T=void 0===A?0:A,C=U("number"!=typeof T?T:G(T,l)),O=y===f?p:f,k=t.rects.popper,D=t.elements[E?O:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ft(V(t)),i=["absolute","fixed"].indexOf(z(t).position)>=0&&S(t)?K(t):t;return L(i)?e.filter((function(t){return L(t)&&W(t,i)&&"body"!==x(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=mt(t,i,n);return e.top=N(s.top,e.top),e.right=P(s.right,e.right),e.bottom=P(s.bottom,e.bottom),e.left=N(s.left,e.left),e}),mt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(L(D)?D:D.contextElement||q(t.elements.popper),g,b,h),I=H(t.elements.reference),M=gt({reference:I,element:k,strategy:"absolute",placement:a}),j=pt(Object.assign({},k,M)),F=y===f?j:I,B={top:$.top-F.top+C.top,bottom:F.bottom-$.bottom+C.bottom,left:$.left-F.left+C.left,right:F.right-$.right+C.right},R=t.modifiersData.offset;if(y===f&&R){var Y=R[a];Object.keys(B).forEach((function(t){var e=[o,s].indexOf(t)>=0?1:-1,i=[n,s].indexOf(t)>=0?"y":"x";B[t]+=Y[i]*e}))}return B}const bt={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=i.mainAxis,u=void 0===d||d,f=i.altAxis,p=void 0===f||f,_=i.fallbackPlacements,b=i.padding,v=i.boundary,y=i.rootBoundary,w=i.altBoundary,E=i.flipVariations,A=void 0===E||E,T=i.allowedAutoPlacements,C=e.options.placement,O=I(C),x=_||(O!==C&&A?function(t){if(I(t)===a)return[];var e=rt(t);return[lt(t),e,lt(e)]}(C):[rt(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat(I(i)===a?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?g:c,d=Z(n),u=d?a?m:m.filter((function(t){return Z(t)===d})):l,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var p=f.reduce((function(e,i){return e[i]=_t(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[I(i)],e}),{});return Object.keys(p).sort((function(t,e){return p[t]-p[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,$=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=_t(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?o:r:F?s:n;L[B]>S[B]&&(z=rt(z));var R=rt(z),q=[];if(u&&q.push(W[j]<=0),p&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,$=!1;break}D.set(M,q)}if($)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function vt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function yt(t){return[n,o,s,r].some((function(e){return t[e]>=0}))}const wt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=_t(e,{elementContext:"reference"}),a=_t(e,{altBoundary:!0}),l=vt(r,n),c=vt(a,s,o),h=yt(l),d=yt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Et={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,s=t.name,a=i.offset,l=void 0===a?[0,0]:a,c=g.reduce((function(t,i){return t[i]=function(t,e,i){var s=I(t),a=[r,n].indexOf(s)>=0?-1:1,l="function"==typeof i?i(Object.assign({},e,{placement:t})):i,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[r,o].indexOf(s)>=0?{x:h,y:c}:{x:c,y:h}}(i,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[s]=c}},At={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=gt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Tt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,a=t.name,l=i.mainAxis,h=void 0===l||l,d=i.altAxis,u=void 0!==d&&d,f=i.boundary,p=i.rootBoundary,m=i.altBoundary,g=i.padding,_=i.tether,b=void 0===_||_,v=i.tetherOffset,y=void 0===v?0:v,w=_t(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=I(e.placement),A=Z(e.placement),T=!A,C=Q(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),$=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?n:r,H="y"===C?s:o,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[H],V=b?-L[W]/2:0,Y=A===c?k[W]:L[W],U=A===c?-L[W]:-k[W],G=e.elements.arrow,J=b&&G?B(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[H],nt=X(0,k[W],J[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:Y-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&K(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==$?void 0:$[C])?j:0,ct=z+ot-lt,ht=X(b?P(R,z+st-lt-at):R,z,b?N(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?n:r,ft="x"===C?s:o,pt=x[O],mt="y"===O?"height":"width",gt=pt+w[ut],bt=pt-w[ft],vt=-1!==[n,r].indexOf(E),yt=null!=(dt=null==$?void 0:$[O])?dt:0,wt=vt?gt:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=X(t,e,i);return n>i?i:n}(wt,pt,Et):X(b?wt:gt,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Ct(t,e,i){void 0===i&&(i=!1);var n,s,o=S(e),r=S(e)&&function(t){var e=t.getBoundingClientRect(),i=M(e.width)/t.offsetWidth||1,n=M(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=q(e),l=H(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==x(e)||dt(a))&&(c=(n=e)!==k(n)&&S(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ct(n)),S(e)?((h=H(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ht(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ot(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var xt={placement:"bottom",modifiers:[],strategy:"absolute"};function kt(){for(var t=arguments.length,e=new Array(t),i=0;iIt.has(t)&&It.get(t).get(e)||null,remove(t,e){if(!It.has(t))return;const i=It.get(t);i.delete(e),0===i.size&&It.delete(t)}},Pt="transitionend",Mt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),jt=t=>{t.dispatchEvent(new Event(Pt))},Ft=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ht=t=>Ft(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Mt(t)):null,Bt=t=>{if(!Ft(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Wt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),zt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?zt(t.parentNode):null},Rt=()=>{},qt=t=>{t.offsetHeight},Vt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Yt=[],Kt=()=>"rtl"===document.documentElement.dir,Qt=t=>{var e;e=()=>{const e=Vt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Yt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Yt)t()})),Yt.push(e)):e()},Xt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Ut=(t,e,i=!0)=>{if(!i)return void Xt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Pt,o),Xt(t))};e.addEventListener(Pt,o),setTimeout((()=>{s||jt(e)}),n)},Gt=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Jt=/[^.]*(?=\..*)\.|.*/,Zt=/\..*/,te=/::\d+$/,ee={};let ie=1;const ne={mouseenter:"mouseover",mouseleave:"mouseout"},se=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function oe(t,e){return e&&`${e}::${ie++}`||t.uidEvent||ie++}function re(t){const e=oe(t);return t.uidEvent=e,ee[e]=ee[e]||{},ee[e]}function ae(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function le(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=ue(t);return se.has(o)||(o=t),[n,s,o]}function ce(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=le(e,i,n);if(e in ne){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=re(t),c=l[a]||(l[a]={}),h=ae(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=oe(r,e.replace(Jt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return pe(s,{delegateTarget:r}),n.oneOff&&fe.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return pe(n,{delegateTarget:t}),i.oneOff&&fe.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function he(t,e,i,n,s){const o=ae(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function de(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&he(t,e,i,r.callable,r.delegationSelector)}function ue(t){return t=t.replace(Zt,""),ne[t]||t}const fe={on(t,e,i,n){ce(t,e,i,n,!1)},one(t,e,i,n){ce(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=le(e,i,n),a=r!==e,l=re(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))de(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(te,"");a&&!e.includes(s)||he(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;he(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=Vt();let s=null,o=!0,r=!0,a=!1;e!==ue(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=pe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function pe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function me(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function ge(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const _e={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${ge(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${ge(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=me(t.dataset[n])}return e},getDataAttribute:(t,e)=>me(t.getAttribute(`data-bs-${ge(e)}`))};class be{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=Ft(e)?_e.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...Ft(e)?_e.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=Ft(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class ve extends be{constructor(t,e){super(),(t=Ht(t))&&(this._element=t,this._config=this._getConfig(e),Nt.set(this._element,this.constructor.DATA_KEY,this))}dispose(){Nt.remove(this._element,this.constructor.DATA_KEY),fe.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Ut(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return Nt.get(Ht(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ye=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>Mt(t))).join(","):null},we={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Wt(t)&&Bt(t)))},getSelectorFromElement(t){const e=ye(t);return e&&we.findOne(e)?e:null},getElementFromSelector(t){const e=ye(t);return e?we.findOne(e):null},getMultipleElementsFromSelector(t){const e=ye(t);return e?we.find(e):[]}},Ee=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;fe.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Wt(this))return;const s=we.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ae=".bs.alert",Te=`close${Ae}`,Ce=`closed${Ae}`;class Oe extends ve{static get NAME(){return"alert"}close(){if(fe.trigger(this._element,Te).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),fe.trigger(this._element,Ce),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Oe.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}Ee(Oe,"close"),Qt(Oe);const xe='[data-bs-toggle="button"]';class ke extends ve{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=ke.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}fe.on(document,"click.bs.button.data-api",xe,(t=>{t.preventDefault();const e=t.target.closest(xe);ke.getOrCreateInstance(e).toggle()})),Qt(ke);const Le=".bs.swipe",Se=`touchstart${Le}`,De=`touchmove${Le}`,$e=`touchend${Le}`,Ie=`pointerdown${Le}`,Ne=`pointerup${Le}`,Pe={endCallback:null,leftCallback:null,rightCallback:null},Me={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class je extends be{constructor(t,e){super(),this._element=t,t&&je.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Pe}static get DefaultType(){return Me}static get NAME(){return"swipe"}dispose(){fe.off(this._element,Le)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Xt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Xt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(fe.on(this._element,Ie,(t=>this._start(t))),fe.on(this._element,Ne,(t=>this._end(t))),this._element.classList.add("pointer-event")):(fe.on(this._element,Se,(t=>this._start(t))),fe.on(this._element,De,(t=>this._move(t))),fe.on(this._element,$e,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Fe=".bs.carousel",He=".data-api",Be="ArrowLeft",We="ArrowRight",ze="next",Re="prev",qe="left",Ve="right",Ye=`slide${Fe}`,Ke=`slid${Fe}`,Qe=`keydown${Fe}`,Xe=`mouseenter${Fe}`,Ue=`mouseleave${Fe}`,Ge=`dragstart${Fe}`,Je=`load${Fe}${He}`,Ze=`click${Fe}${He}`,ti="carousel",ei="active",ii=".active",ni=".carousel-item",si=ii+ni,oi={[Be]:Ve,[We]:qe},ri={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ai={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class li extends ve{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=we.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===ti&&this.cycle()}static get Default(){return ri}static get DefaultType(){return ai}static get NAME(){return"carousel"}next(){this._slide(ze)}nextWhenVisible(){!document.hidden&&Bt(this._element)&&this.next()}prev(){this._slide(Re)}pause(){this._isSliding&&jt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?fe.one(this._element,Ke,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void fe.one(this._element,Ke,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ze:Re;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&fe.on(this._element,Qe,(t=>this._keydown(t))),"hover"===this._config.pause&&(fe.on(this._element,Xe,(()=>this.pause())),fe.on(this._element,Ue,(()=>this._maybeEnableCycle()))),this._config.touch&&je.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of we.find(".carousel-item img",this._element))fe.on(t,Ge,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(qe)),rightCallback:()=>this._slide(this._directionToOrder(Ve)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new je(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=oi[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=we.findOne(ii,this._indicatorsElement);e.classList.remove(ei),e.removeAttribute("aria-current");const i=we.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(ei),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ze,s=e||Gt(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>fe.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Ye).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),qt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(ei),i.classList.remove(ei,c,l),this._isSliding=!1,r(Ke)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return we.findOne(si,this._element)}_getItems(){return we.find(ni,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Kt()?t===qe?Re:ze:t===qe?ze:Re}_orderToDirection(t){return Kt()?t===Re?qe:Ve:t===Re?Ve:qe}static jQueryInterface(t){return this.each((function(){const e=li.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}fe.on(document,Ze,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=we.getElementFromSelector(this);if(!e||!e.classList.contains(ti))return;t.preventDefault();const i=li.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===_e.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),fe.on(window,Je,(()=>{const t=we.find('[data-bs-ride="carousel"]');for(const e of t)li.getOrCreateInstance(e)})),Qt(li);const ci=".bs.collapse",hi=`show${ci}`,di=`shown${ci}`,ui=`hide${ci}`,fi=`hidden${ci}`,pi=`click${ci}.data-api`,mi="show",gi="collapse",_i="collapsing",bi=`:scope .${gi} .${gi}`,vi='[data-bs-toggle="collapse"]',yi={parent:null,toggle:!0},wi={parent:"(null|element)",toggle:"boolean"};class Ei extends ve{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=we.find(vi);for(const t of i){const e=we.getSelectorFromElement(t),i=we.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return yi}static get DefaultType(){return wi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Ei.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(fe.trigger(this._element,hi).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(gi),this._element.classList.add(_i),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi,mi),this._element.style[e]="",fe.trigger(this._element,di)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(fe.trigger(this._element,ui).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,qt(this._element),this._element.classList.add(_i),this._element.classList.remove(gi,mi);for(const t of this._triggerArray){const e=we.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi),fe.trigger(this._element,fi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(mi)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ht(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(vi);for(const e of t){const t=we.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=we.find(bi,this._config.parent);return we.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Ei.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}fe.on(document,pi,vi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of we.getMultipleElementsFromSelector(this))Ei.getOrCreateInstance(t,{toggle:!1}).toggle()})),Qt(Ei);const Ai="dropdown",Ti=".bs.dropdown",Ci=".data-api",Oi="ArrowUp",xi="ArrowDown",ki=`hide${Ti}`,Li=`hidden${Ti}`,Si=`show${Ti}`,Di=`shown${Ti}`,$i=`click${Ti}${Ci}`,Ii=`keydown${Ti}${Ci}`,Ni=`keyup${Ti}${Ci}`,Pi="show",Mi='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',ji=`${Mi}.${Pi}`,Fi=".dropdown-menu",Hi=Kt()?"top-end":"top-start",Bi=Kt()?"top-start":"top-end",Wi=Kt()?"bottom-end":"bottom-start",zi=Kt()?"bottom-start":"bottom-end",Ri=Kt()?"left-start":"right-start",qi=Kt()?"right-start":"left-start",Vi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Yi={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Ki extends ve{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=we.next(this._element,Fi)[0]||we.prev(this._element,Fi)[0]||we.findOne(Fi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Vi}static get DefaultType(){return Yi}static get NAME(){return Ai}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Wt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!fe.trigger(this._element,Si,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Pi),this._element.classList.add(Pi),fe.trigger(this._element,Di,t)}}hide(){if(Wt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!fe.trigger(this._element,ki,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._popper&&this._popper.destroy(),this._menu.classList.remove(Pi),this._element.classList.remove(Pi),this._element.setAttribute("aria-expanded","false"),_e.removeDataAttribute(this._menu,"popper"),fe.trigger(this._element,Li,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!Ft(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ai.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:Ft(this._config.reference)?t=Ht(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=Dt(t,this._menu,i)}_isShown(){return this._menu.classList.contains(Pi)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Ri;if(t.classList.contains("dropstart"))return qi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?Bi:Hi:e?zi:Wi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(_e.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Xt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=we.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Bt(t)));i.length&&Gt(i,e,t===xi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=we.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Oi,xi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Mi)?this:we.prev(this,Mi)[0]||we.next(this,Mi)[0]||we.findOne(Mi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}fe.on(document,Ii,Mi,Ki.dataApiKeydownHandler),fe.on(document,Ii,Fi,Ki.dataApiKeydownHandler),fe.on(document,$i,Ki.clearMenus),fe.on(document,Ni,Ki.clearMenus),fe.on(document,$i,Mi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),Qt(Ki);const Qi="backdrop",Xi="show",Ui=`mousedown.bs.${Qi}`,Gi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ji={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Zi extends be{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Gi}static get DefaultType(){return Ji}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void Xt(t);this._append();const e=this._getElement();this._config.isAnimated&&qt(e),e.classList.add(Xi),this._emulateAnimation((()=>{Xt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),Xt(t)}))):Xt(t)}dispose(){this._isAppended&&(fe.off(this._element,Ui),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ht(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),fe.on(t,Ui,(()=>{Xt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Ut(t,this._getElement(),this._config.isAnimated)}}const tn=".bs.focustrap",en=`focusin${tn}`,nn=`keydown.tab${tn}`,sn="backward",on={autofocus:!0,trapElement:null},rn={autofocus:"boolean",trapElement:"element"};class an extends be{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return on}static get DefaultType(){return rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),fe.off(document,tn),fe.on(document,en,(t=>this._handleFocusin(t))),fe.on(document,nn,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,fe.off(document,tn))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=we.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===sn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?sn:"forward")}}const ln=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",cn=".sticky-top",hn="padding-right",dn="margin-right";class un{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,hn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e+t)),this._setElementAttributes(cn,dn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,hn),this._resetElementAttributes(ln,hn),this._resetElementAttributes(cn,dn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&_e.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=_e.getDataAttribute(t,e);null!==i?(_e.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(Ft(t))e(t);else for(const i of we.find(t,this._element))e(i)}}const fn=".bs.modal",pn=`hide${fn}`,mn=`hidePrevented${fn}`,gn=`hidden${fn}`,_n=`show${fn}`,bn=`shown${fn}`,vn=`resize${fn}`,yn=`click.dismiss${fn}`,wn=`mousedown.dismiss${fn}`,En=`keydown.dismiss${fn}`,An=`click${fn}.data-api`,Tn="modal-open",Cn="show",On="modal-static",xn={backdrop:!0,focus:!0,keyboard:!0},kn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ln extends ve{constructor(t,e){super(t,e),this._dialog=we.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new un,this._addEventListeners()}static get Default(){return xn}static get DefaultType(){return kn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||fe.trigger(this._element,_n,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Tn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(fe.trigger(this._element,pn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Cn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){fe.off(window,fn),fe.off(this._dialog,fn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new an({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=we.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),qt(this._element),this._element.classList.add(Cn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,fe.trigger(this._element,bn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){fe.on(this._element,En,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),fe.on(window,vn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),fe.on(this._element,wn,(t=>{fe.one(this._element,yn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Tn),this._resetAdjustments(),this._scrollBar.reset(),fe.trigger(this._element,gn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(fe.trigger(this._element,mn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(On)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(On),this._queueCallback((()=>{this._element.classList.remove(On),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Kt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Kt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}fe.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=we.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),fe.one(e,_n,(t=>{t.defaultPrevented||fe.one(e,gn,(()=>{Bt(this)&&this.focus()}))}));const i=we.findOne(".modal.show");i&&Ln.getInstance(i).hide(),Ln.getOrCreateInstance(e).toggle(this)})),Ee(Ln),Qt(Ln);const Sn=".bs.offcanvas",Dn=".data-api",$n=`load${Sn}${Dn}`,In="show",Nn="showing",Pn="hiding",Mn=".offcanvas.show",jn=`show${Sn}`,Fn=`shown${Sn}`,Hn=`hide${Sn}`,Bn=`hidePrevented${Sn}`,Wn=`hidden${Sn}`,zn=`resize${Sn}`,Rn=`click${Sn}${Dn}`,qn=`keydown.dismiss${Sn}`,Vn={backdrop:!0,keyboard:!0,scroll:!1},Yn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends ve{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Vn}static get DefaultType(){return Yn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||fe.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new un).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Nn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(In),this._element.classList.remove(Nn),fe.trigger(this._element,Fn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(fe.trigger(this._element,Hn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Pn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(In,Pn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new un).reset(),fe.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Zi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():fe.trigger(this._element,Bn)}:null})}_initializeFocusTrap(){return new an({trapElement:this._element})}_addEventListeners(){fe.on(this._element,qn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():fe.trigger(this._element,Bn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}fe.on(document,Rn,'[data-bs-toggle="offcanvas"]',(function(t){const e=we.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this))return;fe.one(e,Wn,(()=>{Bt(this)&&this.focus()}));const i=we.findOne(Mn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),fe.on(window,$n,(()=>{for(const t of we.find(Mn))Kn.getOrCreateInstance(t).show()})),fe.on(window,zn,(()=>{for(const t of we.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),Ee(Kn),Qt(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Un=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Gn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Un.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Jn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Zn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ts={entry:"(string|element|function|null)",selector:"(string|element)"};class es extends be{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Jn}static get DefaultType(){return Zn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},ts)}_setContent(t,e,i){const n=we.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?Ft(e)?this._putElementInTemplate(Ht(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Gn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Xt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const is=new Set(["sanitize","allowList","sanitizeFn"]),ns="fade",ss="show",os=".tooltip-inner",rs=".modal",as="hide.bs.modal",ls="hover",cs="focus",hs={AUTO:"auto",TOP:"top",RIGHT:Kt()?"left":"right",BOTTOM:"bottom",LEFT:Kt()?"right":"left"},ds={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},us={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class fs extends ve{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return ds}static get DefaultType(){return us}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),fe.off(this._element.closest(rs),as,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=fe.trigger(this._element,this.constructor.eventName("show")),e=(zt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),fe.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._queueCallback((()=>{fe.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!fe.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._activeTrigger.click=!1,this._activeTrigger[cs]=!1,this._activeTrigger[ls]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),fe.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ns,ss),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ns),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new es({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[os]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ns)}_isShown(){return this.tip&&this.tip.classList.contains(ss)}_createPopper(t){const e=Xt(this._config.placement,[this,t,this._element]),i=hs[e.toUpperCase()];return Dt(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Xt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Xt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)fe.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ls?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ls?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");fe.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?cs:ls]=!0,e._enter()})),fe.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?cs:ls]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},fe.on(this._element.closest(rs),as,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=_e.getDataAttributes(this._element);for(const t of Object.keys(e))is.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ht(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=fs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(fs);const ps=".popover-header",ms=".popover-body",gs={...fs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},_s={...fs.DefaultType,content:"(null|string|element|function)"};class bs extends fs{static get Default(){return gs}static get DefaultType(){return _s}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[ps]:this._getTitle(),[ms]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=bs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(bs);const vs=".bs.scrollspy",ys=`activate${vs}`,ws=`click${vs}`,Es=`load${vs}.data-api`,As="active",Ts="[href]",Cs=".nav-link",Os=`${Cs}, .nav-item > ${Cs}, .list-group-item`,xs={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},ks={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ls extends ve{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return xs}static get DefaultType(){return ks}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ht(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(fe.off(this._config.target,ws),fe.on(this._config.target,ws,Ts,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=we.find(Ts,this._config.target);for(const e of t){if(!e.hash||Wt(e))continue;const t=we.findOne(decodeURI(e.hash),this._element);Bt(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),fe.trigger(this._element,ys,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))we.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of we.parents(t,".nav, .list-group"))for(const t of we.prev(e,Os))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=we.find(`${Ts}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=Ls.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(window,Es,(()=>{for(const t of we.find('[data-bs-spy="scroll"]'))Ls.getOrCreateInstance(t)})),Qt(Ls);const Ss=".bs.tab",Ds=`hide${Ss}`,$s=`hidden${Ss}`,Is=`show${Ss}`,Ns=`shown${Ss}`,Ps=`click${Ss}`,Ms=`keydown${Ss}`,js=`load${Ss}`,Fs="ArrowLeft",Hs="ArrowRight",Bs="ArrowUp",Ws="ArrowDown",zs="Home",Rs="End",qs="active",Vs="fade",Ys="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Us=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Gs=`.${qs}[data-bs-toggle="tab"], .${qs}[data-bs-toggle="pill"], .${qs}[data-bs-toggle="list"]`;class Js extends ve{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),fe.on(this._element,Ms,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?fe.trigger(e,Ds,{relatedTarget:t}):null;fe.trigger(t,Is,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(qs),this._activate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),fe.trigger(t,Ns,{relatedTarget:e})):t.classList.add(Ys)}),t,t.classList.contains(Vs)))}_deactivate(t,e){t&&(t.classList.remove(qs),t.blur(),this._deactivate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),fe.trigger(t,$s,{relatedTarget:e})):t.classList.remove(Ys)}),t,t.classList.contains(Vs)))}_keydown(t){if(![Fs,Hs,Bs,Ws,zs,Rs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Wt(t)));let i;if([zs,Rs].includes(t.key))i=e[t.key===zs?0:e.length-1];else{const n=[Hs,Ws].includes(t.key);i=Gt(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Js.getOrCreateInstance(i).show())}_getChildren(){return we.find(Us,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=we.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=we.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,qs),n(".dropdown-menu",Ys),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(qs)}_getInnerElement(t){return t.matches(Us)?t:we.findOne(Us,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Js.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(document,Ps,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this)||Js.getOrCreateInstance(this).show()})),fe.on(window,js,(()=>{for(const t of we.find(Gs))Js.getOrCreateInstance(t)})),Qt(Js);const Zs=".bs.toast",to=`mouseover${Zs}`,eo=`mouseout${Zs}`,io=`focusin${Zs}`,no=`focusout${Zs}`,so=`hide${Zs}`,oo=`hidden${Zs}`,ro=`show${Zs}`,ao=`shown${Zs}`,lo="hide",co="show",ho="showing",uo={animation:"boolean",autohide:"boolean",delay:"number"},fo={animation:!0,autohide:!0,delay:5e3};class po extends ve{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return fo}static get DefaultType(){return uo}static get NAME(){return"toast"}show(){fe.trigger(this._element,ro).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(lo),qt(this._element),this._element.classList.add(co,ho),this._queueCallback((()=>{this._element.classList.remove(ho),fe.trigger(this._element,ao),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(fe.trigger(this._element,so).defaultPrevented||(this._element.classList.add(ho),this._queueCallback((()=>{this._element.classList.add(lo),this._element.classList.remove(ho,co),fe.trigger(this._element,oo)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(co),super.dispose()}isShown(){return this._element.classList.contains(co)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){fe.on(this._element,to,(t=>this._onInteraction(t,!0))),fe.on(this._element,eo,(t=>this._onInteraction(t,!1))),fe.on(this._element,io,(t=>this._onInteraction(t,!0))),fe.on(this._element,no,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=po.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function mo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}Ee(po),Qt(po),mo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new fs(t,{delay:{show:500,hide:100}})}))})),mo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),mo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))})),window.bootstrap=i})(); +//# sourceMappingURL=bootstrap.js.map \ No newline at end of file diff --git a/_static/scripts/bootstrap.js.LICENSE.txt b/_static/scripts/bootstrap.js.LICENSE.txt new file mode 100644 index 0000000000..28755c2c5b --- /dev/null +++ b/_static/scripts/bootstrap.js.LICENSE.txt @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/_static/scripts/bootstrap.js.map b/_static/scripts/bootstrap.js.map new file mode 100644 index 0000000000..4a3502aeb2 --- /dev/null +++ b/_static/scripts/bootstrap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/bootstrap.js","mappings":";mBACA,IAAIA,EAAsB,CCA1BA,EAAwB,CAACC,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXF,EAAoBI,EAAEF,EAAYC,KAASH,EAAoBI,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDH,EAAwB,CAACS,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFV,EAAyBC,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,GAAO,01BCLvD,IAAI,EAAM,MACNC,EAAS,SACTC,EAAQ,QACRC,EAAO,OACPC,EAAO,OACPC,EAAiB,CAAC,EAAKJ,EAAQC,EAAOC,GACtCG,EAAQ,QACRC,EAAM,MACNC,EAAkB,kBAClBC,EAAW,WACXC,EAAS,SACTC,EAAY,YACZC,EAAmCP,EAAeQ,QAAO,SAAUC,EAAKC,GACjF,OAAOD,EAAIE,OAAO,CAACD,EAAY,IAAMT,EAAOS,EAAY,IAAMR,GAChE,GAAG,IACQ,EAA0B,GAAGS,OAAOX,EAAgB,CAACD,IAAOS,QAAO,SAAUC,EAAKC,GAC3F,OAAOD,EAAIE,OAAO,CAACD,EAAWA,EAAY,IAAMT,EAAOS,EAAY,IAAMR,GAC3E,GAAG,IAEQU,EAAa,aACbC,EAAO,OACPC,EAAY,YAEZC,EAAa,aACbC,EAAO,OACPC,EAAY,YAEZC,EAAc,cACdC,EAAQ,QACRC,EAAa,aACbC,EAAiB,CAACT,EAAYC,EAAMC,EAAWC,EAAYC,EAAMC,EAAWC,EAAaC,EAAOC,GC9B5F,SAASE,EAAYC,GAClC,OAAOA,GAAWA,EAAQC,UAAY,IAAIC,cAAgB,IAC5D,CCFe,SAASC,EAAUC,GAChC,GAAY,MAARA,EACF,OAAOC,OAGT,GAAwB,oBAApBD,EAAKE,WAAkC,CACzC,IAAIC,EAAgBH,EAAKG,cACzB,OAAOA,GAAgBA,EAAcC,aAAwBH,MAC/D,CAEA,OAAOD,CACT,CCTA,SAASK,EAAUL,GAEjB,OAAOA,aADUD,EAAUC,GAAMM,SACIN,aAAgBM,OACvD,CAEA,SAASC,EAAcP,GAErB,OAAOA,aADUD,EAAUC,GAAMQ,aACIR,aAAgBQ,WACvD,CAEA,SAASC,EAAaT,GAEpB,MAA0B,oBAAfU,aAKJV,aADUD,EAAUC,GAAMU,YACIV,aAAgBU,WACvD,CCwDA,SACEC,KAAM,cACNC,SAAS,EACTC,MAAO,QACPC,GA5EF,SAAqBC,GACnB,IAAIC,EAAQD,EAAKC,MACjB3D,OAAO4D,KAAKD,EAAME,UAAUC,SAAQ,SAAUR,GAC5C,IAAIS,EAAQJ,EAAMK,OAAOV,IAAS,CAAC,EAC/BW,EAAaN,EAAMM,WAAWX,IAAS,CAAC,EACxCf,EAAUoB,EAAME,SAASP,GAExBJ,EAAcX,IAAaD,EAAYC,KAO5CvC,OAAOkE,OAAO3B,EAAQwB,MAAOA,GAC7B/D,OAAO4D,KAAKK,GAAYH,SAAQ,SAAUR,GACxC,IAAI3C,EAAQsD,EAAWX,IAET,IAAV3C,EACF4B,EAAQ4B,gBAAgBb,GAExBf,EAAQ6B,aAAad,GAAgB,IAAV3C,EAAiB,GAAKA,EAErD,IACF,GACF,EAoDE0D,OAlDF,SAAgBC,GACd,IAAIX,EAAQW,EAAMX,MACdY,EAAgB,CAClBlD,OAAQ,CACNmD,SAAUb,EAAMc,QAAQC,SACxB5D,KAAM,IACN6D,IAAK,IACLC,OAAQ,KAEVC,MAAO,CACLL,SAAU,YAEZlD,UAAW,CAAC,GASd,OAPAtB,OAAOkE,OAAOP,EAAME,SAASxC,OAAO0C,MAAOQ,EAAclD,QACzDsC,EAAMK,OAASO,EAEXZ,EAAME,SAASgB,OACjB7E,OAAOkE,OAAOP,EAAME,SAASgB,MAAMd,MAAOQ,EAAcM,OAGnD,WACL7E,OAAO4D,KAAKD,EAAME,UAAUC,SAAQ,SAAUR,GAC5C,IAAIf,EAAUoB,EAAME,SAASP,GACzBW,EAAaN,EAAMM,WAAWX,IAAS,CAAC,EAGxCS,EAFkB/D,OAAO4D,KAAKD,EAAMK,OAAOzD,eAAe+C,GAAQK,EAAMK,OAAOV,GAAQiB,EAAcjB,IAE7E9B,QAAO,SAAUuC,EAAOe,GAElD,OADAf,EAAMe,GAAY,GACXf,CACT,GAAG,CAAC,GAECb,EAAcX,IAAaD,EAAYC,KAI5CvC,OAAOkE,OAAO3B,EAAQwB,MAAOA,GAC7B/D,OAAO4D,KAAKK,GAAYH,SAAQ,SAAUiB,GACxCxC,EAAQ4B,gBAAgBY,EAC1B,IACF,GACF,CACF,EASEC,SAAU,CAAC,kBCjFE,SAASC,EAAiBvD,GACvC,OAAOA,EAAUwD,MAAM,KAAK,EAC9B,CCHO,IAAI,EAAMC,KAAKC,IACX,EAAMD,KAAKE,IACXC,EAAQH,KAAKG,MCFT,SAASC,IACtB,IAAIC,EAASC,UAAUC,cAEvB,OAAc,MAAVF,GAAkBA,EAAOG,QAAUC,MAAMC,QAAQL,EAAOG,QACnDH,EAAOG,OAAOG,KAAI,SAAUC,GACjC,OAAOA,EAAKC,MAAQ,IAAMD,EAAKE,OACjC,IAAGC,KAAK,KAGHT,UAAUU,SACnB,CCTe,SAASC,IACtB,OAAQ,iCAAiCC,KAAKd,IAChD,CCCe,SAASe,EAAsB/D,EAASgE,EAAcC,QAC9C,IAAjBD,IACFA,GAAe,QAGO,IAApBC,IACFA,GAAkB,GAGpB,IAAIC,EAAalE,EAAQ+D,wBACrBI,EAAS,EACTC,EAAS,EAETJ,GAAgBrD,EAAcX,KAChCmE,EAASnE,EAAQqE,YAAc,GAAItB,EAAMmB,EAAWI,OAAStE,EAAQqE,aAAmB,EACxFD,EAASpE,EAAQuE,aAAe,GAAIxB,EAAMmB,EAAWM,QAAUxE,EAAQuE,cAAoB,GAG7F,IACIE,GADOhE,EAAUT,GAAWG,EAAUH,GAAWK,QAC3BoE,eAEtBC,GAAoBb,KAAsBI,EAC1CU,GAAKT,EAAW3F,MAAQmG,GAAoBD,EAAiBA,EAAeG,WAAa,IAAMT,EAC/FU,GAAKX,EAAW9B,KAAOsC,GAAoBD,EAAiBA,EAAeK,UAAY,IAAMV,EAC7FE,EAAQJ,EAAWI,MAAQH,EAC3BK,EAASN,EAAWM,OAASJ,EACjC,MAAO,CACLE,MAAOA,EACPE,OAAQA,EACRpC,IAAKyC,EACLvG,MAAOqG,EAAIL,EACXjG,OAAQwG,EAAIL,EACZjG,KAAMoG,EACNA,EAAGA,EACHE,EAAGA,EAEP,CCrCe,SAASE,EAAc/E,GACpC,IAAIkE,EAAaH,EAAsB/D,GAGnCsE,EAAQtE,EAAQqE,YAChBG,EAASxE,EAAQuE,aAUrB,OARI3B,KAAKoC,IAAId,EAAWI,MAAQA,IAAU,IACxCA,EAAQJ,EAAWI,OAGjB1B,KAAKoC,IAAId,EAAWM,OAASA,IAAW,IAC1CA,EAASN,EAAWM,QAGf,CACLG,EAAG3E,EAAQ4E,WACXC,EAAG7E,EAAQ8E,UACXR,MAAOA,EACPE,OAAQA,EAEZ,CCvBe,SAASS,EAASC,EAAQC,GACvC,IAAIC,EAAWD,EAAME,aAAeF,EAAME,cAE1C,GAAIH,EAAOD,SAASE,GAClB,OAAO,EAEJ,GAAIC,GAAYvE,EAAauE,GAAW,CACzC,IAAIE,EAAOH,EAEX,EAAG,CACD,GAAIG,GAAQJ,EAAOK,WAAWD,GAC5B,OAAO,EAITA,EAAOA,EAAKE,YAAcF,EAAKG,IACjC,OAASH,EACX,CAGF,OAAO,CACT,CCrBe,SAAS,EAAiBtF,GACvC,OAAOG,EAAUH,GAAS0F,iBAAiB1F,EAC7C,CCFe,SAAS2F,EAAe3F,GACrC,MAAO,CAAC,QAAS,KAAM,MAAM4F,QAAQ7F,EAAYC,KAAa,CAChE,CCFe,SAAS6F,EAAmB7F,GAEzC,QAASS,EAAUT,GAAWA,EAAQO,cACtCP,EAAQ8F,WAAazF,OAAOyF,UAAUC,eACxC,CCFe,SAASC,EAAchG,GACpC,MAA6B,SAAzBD,EAAYC,GACPA,EAMPA,EAAQiG,cACRjG,EAAQwF,aACR3E,EAAab,GAAWA,EAAQyF,KAAO,OAEvCI,EAAmB7F,EAGvB,CCVA,SAASkG,EAAoBlG,GAC3B,OAAKW,EAAcX,IACoB,UAAvC,EAAiBA,GAASiC,SAInBjC,EAAQmG,aAHN,IAIX,CAwCe,SAASC,EAAgBpG,GAItC,IAHA,IAAIK,EAASF,EAAUH,GACnBmG,EAAeD,EAAoBlG,GAEhCmG,GAAgBR,EAAeQ,IAA6D,WAA5C,EAAiBA,GAAclE,UACpFkE,EAAeD,EAAoBC,GAGrC,OAAIA,IAA+C,SAA9BpG,EAAYoG,IAA0D,SAA9BpG,EAAYoG,IAAwE,WAA5C,EAAiBA,GAAclE,UAC3H5B,EAGF8F,GAhDT,SAA4BnG,GAC1B,IAAIqG,EAAY,WAAWvC,KAAKd,KAGhC,GAFW,WAAWc,KAAKd,MAEfrC,EAAcX,IAII,UAFX,EAAiBA,GAEnBiC,SACb,OAAO,KAIX,IAAIqE,EAAcN,EAAchG,GAMhC,IAJIa,EAAayF,KACfA,EAAcA,EAAYb,MAGrB9E,EAAc2F,IAAgB,CAAC,OAAQ,QAAQV,QAAQ7F,EAAYuG,IAAgB,GAAG,CAC3F,IAAIC,EAAM,EAAiBD,GAI3B,GAAsB,SAAlBC,EAAIC,WAA4C,SAApBD,EAAIE,aAA0C,UAAhBF,EAAIG,UAAiF,IAA1D,CAAC,YAAa,eAAed,QAAQW,EAAII,aAAsBN,GAAgC,WAAnBE,EAAII,YAA2BN,GAAaE,EAAIK,QAAyB,SAAfL,EAAIK,OACjO,OAAON,EAEPA,EAAcA,EAAYd,UAE9B,CAEA,OAAO,IACT,CAgByBqB,CAAmB7G,IAAYK,CACxD,CCpEe,SAASyG,EAAyB3H,GAC/C,MAAO,CAAC,MAAO,UAAUyG,QAAQzG,IAAc,EAAI,IAAM,GAC3D,CCDO,SAAS4H,EAAOjE,EAAK1E,EAAOyE,GACjC,OAAO,EAAQC,EAAK,EAAQ1E,EAAOyE,GACrC,CCFe,SAASmE,EAAmBC,GACzC,OAAOxJ,OAAOkE,OAAO,CAAC,ECDf,CACLS,IAAK,EACL9D,MAAO,EACPD,OAAQ,EACRE,KAAM,GDHuC0I,EACjD,CEHe,SAASC,EAAgB9I,EAAOiD,GAC7C,OAAOA,EAAKpC,QAAO,SAAUkI,EAAS5J,GAEpC,OADA4J,EAAQ5J,GAAOa,EACR+I,CACT,GAAG,CAAC,EACN,CC4EA,SACEpG,KAAM,QACNC,SAAS,EACTC,MAAO,OACPC,GApEF,SAAeC,GACb,IAAIiG,EAEAhG,EAAQD,EAAKC,MACbL,EAAOI,EAAKJ,KACZmB,EAAUf,EAAKe,QACfmF,EAAejG,EAAME,SAASgB,MAC9BgF,EAAgBlG,EAAMmG,cAAcD,cACpCE,EAAgB9E,EAAiBtB,EAAMjC,WACvCsI,EAAOX,EAAyBU,GAEhCE,EADa,CAACnJ,EAAMD,GAAOsH,QAAQ4B,IAAkB,EAClC,SAAW,QAElC,GAAKH,GAAiBC,EAAtB,CAIA,IAAIL,EAxBgB,SAAyBU,EAASvG,GAItD,OAAO4F,EAAsC,iBAH7CW,EAA6B,mBAAZA,EAAyBA,EAAQlK,OAAOkE,OAAO,CAAC,EAAGP,EAAMwG,MAAO,CAC/EzI,UAAWiC,EAAMjC,aACbwI,GACkDA,EAAUT,EAAgBS,EAASlJ,GAC7F,CAmBsBoJ,CAAgB3F,EAAQyF,QAASvG,GACjD0G,EAAY/C,EAAcsC,GAC1BU,EAAmB,MAATN,EAAe,EAAMlJ,EAC/ByJ,EAAmB,MAATP,EAAepJ,EAASC,EAClC2J,EAAU7G,EAAMwG,MAAM7I,UAAU2I,GAAOtG,EAAMwG,MAAM7I,UAAU0I,GAAQH,EAAcG,GAAQrG,EAAMwG,MAAM9I,OAAO4I,GAC9GQ,EAAYZ,EAAcG,GAAQrG,EAAMwG,MAAM7I,UAAU0I,GACxDU,EAAoB/B,EAAgBiB,GACpCe,EAAaD,EAA6B,MAATV,EAAeU,EAAkBE,cAAgB,EAAIF,EAAkBG,aAAe,EAAI,EAC3HC,EAAoBN,EAAU,EAAIC,EAAY,EAG9CpF,EAAMmE,EAAcc,GACpBlF,EAAMuF,EAAaN,EAAUJ,GAAOT,EAAce,GAClDQ,EAASJ,EAAa,EAAIN,EAAUJ,GAAO,EAAIa,EAC/CE,EAAS1B,EAAOjE,EAAK0F,EAAQ3F,GAE7B6F,EAAWjB,EACfrG,EAAMmG,cAAcxG,KAASqG,EAAwB,CAAC,GAAyBsB,GAAYD,EAAQrB,EAAsBuB,aAAeF,EAASD,EAAQpB,EAnBzJ,CAoBF,EAkCEtF,OAhCF,SAAgBC,GACd,IAAIX,EAAQW,EAAMX,MAEdwH,EADU7G,EAAMG,QACWlC,QAC3BqH,OAAoC,IAArBuB,EAA8B,sBAAwBA,EAErD,MAAhBvB,IAKwB,iBAAjBA,IACTA,EAAejG,EAAME,SAASxC,OAAO+J,cAAcxB,MAOhDpC,EAAS7D,EAAME,SAASxC,OAAQuI,KAIrCjG,EAAME,SAASgB,MAAQ+E,EACzB,EASE5E,SAAU,CAAC,iBACXqG,iBAAkB,CAAC,oBCxFN,SAASC,EAAa5J,GACnC,OAAOA,EAAUwD,MAAM,KAAK,EAC9B,CCOA,IAAIqG,GAAa,CACf5G,IAAK,OACL9D,MAAO,OACPD,OAAQ,OACRE,KAAM,QAeD,SAAS0K,GAAYlH,GAC1B,IAAImH,EAEApK,EAASiD,EAAMjD,OACfqK,EAAapH,EAAMoH,WACnBhK,EAAY4C,EAAM5C,UAClBiK,EAAYrH,EAAMqH,UAClBC,EAAUtH,EAAMsH,QAChBpH,EAAWF,EAAME,SACjBqH,EAAkBvH,EAAMuH,gBACxBC,EAAWxH,EAAMwH,SACjBC,EAAezH,EAAMyH,aACrBC,EAAU1H,EAAM0H,QAChBC,EAAaL,EAAQ1E,EACrBA,OAAmB,IAAf+E,EAAwB,EAAIA,EAChCC,EAAaN,EAAQxE,EACrBA,OAAmB,IAAf8E,EAAwB,EAAIA,EAEhCC,EAAgC,mBAAjBJ,EAA8BA,EAAa,CAC5D7E,EAAGA,EACHE,IACG,CACHF,EAAGA,EACHE,GAGFF,EAAIiF,EAAMjF,EACVE,EAAI+E,EAAM/E,EACV,IAAIgF,EAAOR,EAAQrL,eAAe,KAC9B8L,EAAOT,EAAQrL,eAAe,KAC9B+L,EAAQxL,EACRyL,EAAQ,EACRC,EAAM5J,OAEV,GAAIkJ,EAAU,CACZ,IAAIpD,EAAeC,EAAgBtH,GAC/BoL,EAAa,eACbC,EAAY,cAEZhE,IAAiBhG,EAAUrB,IAGmB,WAA5C,EAFJqH,EAAeN,EAAmB/G,IAECmD,UAAsC,aAAbA,IAC1DiI,EAAa,eACbC,EAAY,gBAOZhL,IAAc,IAAQA,IAAcZ,GAAQY,IAAcb,IAAU8K,IAAczK,KACpFqL,EAAQ3L,EAGRwG,IAFc4E,GAAWtD,IAAiB8D,GAAOA,EAAIxF,eAAiBwF,EAAIxF,eAAeD,OACzF2B,EAAa+D,IACEf,EAAW3E,OAC1BK,GAAKyE,EAAkB,GAAK,GAG1BnK,IAAcZ,IAASY,IAAc,GAAOA,IAAcd,GAAW+K,IAAczK,KACrFoL,EAAQzL,EAGRqG,IAFc8E,GAAWtD,IAAiB8D,GAAOA,EAAIxF,eAAiBwF,EAAIxF,eAAeH,MACzF6B,EAAagE,IACEhB,EAAW7E,MAC1BK,GAAK2E,EAAkB,GAAK,EAEhC,CAEA,IAgBMc,EAhBFC,EAAe5M,OAAOkE,OAAO,CAC/BM,SAAUA,GACTsH,GAAYP,IAEXsB,GAAyB,IAAjBd,EAlFd,SAA2BrI,EAAM8I,GAC/B,IAAItF,EAAIxD,EAAKwD,EACTE,EAAI1D,EAAK0D,EACT0F,EAAMN,EAAIO,kBAAoB,EAClC,MAAO,CACL7F,EAAG5B,EAAM4B,EAAI4F,GAAOA,GAAO,EAC3B1F,EAAG9B,EAAM8B,EAAI0F,GAAOA,GAAO,EAE/B,CA0EsCE,CAAkB,CACpD9F,EAAGA,EACHE,GACC1E,EAAUrB,IAAW,CACtB6F,EAAGA,EACHE,GAMF,OAHAF,EAAI2F,EAAM3F,EACVE,EAAIyF,EAAMzF,EAENyE,EAGK7L,OAAOkE,OAAO,CAAC,EAAG0I,IAAeD,EAAiB,CAAC,GAAkBJ,GAASF,EAAO,IAAM,GAAIM,EAAeL,GAASF,EAAO,IAAM,GAAIO,EAAe5D,WAAayD,EAAIO,kBAAoB,IAAM,EAAI,aAAe7F,EAAI,OAASE,EAAI,MAAQ,eAAiBF,EAAI,OAASE,EAAI,SAAUuF,IAG5R3M,OAAOkE,OAAO,CAAC,EAAG0I,IAAenB,EAAkB,CAAC,GAAmBc,GAASF,EAAOjF,EAAI,KAAO,GAAIqE,EAAgBa,GAASF,EAAOlF,EAAI,KAAO,GAAIuE,EAAgB1C,UAAY,GAAI0C,GAC9L,CA4CA,UACEnI,KAAM,gBACNC,SAAS,EACTC,MAAO,cACPC,GA9CF,SAAuBwJ,GACrB,IAAItJ,EAAQsJ,EAAMtJ,MACdc,EAAUwI,EAAMxI,QAChByI,EAAwBzI,EAAQoH,gBAChCA,OAA4C,IAA1BqB,GAA0CA,EAC5DC,EAAoB1I,EAAQqH,SAC5BA,OAAiC,IAAtBqB,GAAsCA,EACjDC,EAAwB3I,EAAQsH,aAChCA,OAAyC,IAA1BqB,GAA0CA,EACzDR,EAAe,CACjBlL,UAAWuD,EAAiBtB,EAAMjC,WAClCiK,UAAWL,EAAa3H,EAAMjC,WAC9BL,OAAQsC,EAAME,SAASxC,OACvBqK,WAAY/H,EAAMwG,MAAM9I,OACxBwK,gBAAiBA,EACjBG,QAAoC,UAA3BrI,EAAMc,QAAQC,UAGgB,MAArCf,EAAMmG,cAAcD,gBACtBlG,EAAMK,OAAO3C,OAASrB,OAAOkE,OAAO,CAAC,EAAGP,EAAMK,OAAO3C,OAAQmK,GAAYxL,OAAOkE,OAAO,CAAC,EAAG0I,EAAc,CACvGhB,QAASjI,EAAMmG,cAAcD,cAC7BrF,SAAUb,EAAMc,QAAQC,SACxBoH,SAAUA,EACVC,aAAcA,OAIe,MAA7BpI,EAAMmG,cAAcjF,QACtBlB,EAAMK,OAAOa,MAAQ7E,OAAOkE,OAAO,CAAC,EAAGP,EAAMK,OAAOa,MAAO2G,GAAYxL,OAAOkE,OAAO,CAAC,EAAG0I,EAAc,CACrGhB,QAASjI,EAAMmG,cAAcjF,MAC7BL,SAAU,WACVsH,UAAU,EACVC,aAAcA,OAIlBpI,EAAMM,WAAW5C,OAASrB,OAAOkE,OAAO,CAAC,EAAGP,EAAMM,WAAW5C,OAAQ,CACnE,wBAAyBsC,EAAMjC,WAEnC,EAQE2L,KAAM,CAAC,GCrKT,IAAIC,GAAU,CACZA,SAAS,GAsCX,UACEhK,KAAM,iBACNC,SAAS,EACTC,MAAO,QACPC,GAAI,WAAe,EACnBY,OAxCF,SAAgBX,GACd,IAAIC,EAAQD,EAAKC,MACb4J,EAAW7J,EAAK6J,SAChB9I,EAAUf,EAAKe,QACf+I,EAAkB/I,EAAQgJ,OAC1BA,OAA6B,IAApBD,GAAoCA,EAC7CE,EAAkBjJ,EAAQkJ,OAC1BA,OAA6B,IAApBD,GAAoCA,EAC7C9K,EAASF,EAAUiB,EAAME,SAASxC,QAClCuM,EAAgB,GAAGjM,OAAOgC,EAAMiK,cAActM,UAAWqC,EAAMiK,cAAcvM,QAYjF,OAVIoM,GACFG,EAAc9J,SAAQ,SAAU+J,GAC9BA,EAAaC,iBAAiB,SAAUP,EAASQ,OAAQT,GAC3D,IAGEK,GACF/K,EAAOkL,iBAAiB,SAAUP,EAASQ,OAAQT,IAG9C,WACDG,GACFG,EAAc9J,SAAQ,SAAU+J,GAC9BA,EAAaG,oBAAoB,SAAUT,EAASQ,OAAQT,GAC9D,IAGEK,GACF/K,EAAOoL,oBAAoB,SAAUT,EAASQ,OAAQT,GAE1D,CACF,EASED,KAAM,CAAC,GC/CT,IAAIY,GAAO,CACTnN,KAAM,QACND,MAAO,OACPD,OAAQ,MACR+D,IAAK,UAEQ,SAASuJ,GAAqBxM,GAC3C,OAAOA,EAAUyM,QAAQ,0BAA0B,SAAUC,GAC3D,OAAOH,GAAKG,EACd,GACF,CCVA,IAAI,GAAO,CACTnN,MAAO,MACPC,IAAK,SAEQ,SAASmN,GAA8B3M,GACpD,OAAOA,EAAUyM,QAAQ,cAAc,SAAUC,GAC/C,OAAO,GAAKA,EACd,GACF,CCPe,SAASE,GAAgB3L,GACtC,IAAI6J,EAAM9J,EAAUC,GAGpB,MAAO,CACL4L,WAHe/B,EAAIgC,YAInBC,UAHcjC,EAAIkC,YAKtB,CCNe,SAASC,GAAoBpM,GAQ1C,OAAO+D,EAAsB8B,EAAmB7F,IAAUzB,KAAOwN,GAAgB/L,GAASgM,UAC5F,CCXe,SAASK,GAAerM,GAErC,IAAIsM,EAAoB,EAAiBtM,GACrCuM,EAAWD,EAAkBC,SAC7BC,EAAYF,EAAkBE,UAC9BC,EAAYH,EAAkBG,UAElC,MAAO,6BAA6B3I,KAAKyI,EAAWE,EAAYD,EAClE,CCLe,SAASE,GAAgBtM,GACtC,MAAI,CAAC,OAAQ,OAAQ,aAAawF,QAAQ7F,EAAYK,KAAU,EAEvDA,EAAKG,cAAcoM,KAGxBhM,EAAcP,IAASiM,GAAejM,GACjCA,EAGFsM,GAAgB1G,EAAc5F,GACvC,CCJe,SAASwM,GAAkB5M,EAAS6M,GACjD,IAAIC,OAES,IAATD,IACFA,EAAO,IAGT,IAAIvB,EAAeoB,GAAgB1M,GAC/B+M,EAASzB,KAAqE,OAAlDwB,EAAwB9M,EAAQO,oBAAyB,EAASuM,EAAsBH,MACpH1C,EAAM9J,EAAUmL,GAChB0B,EAASD,EAAS,CAAC9C,GAAK7K,OAAO6K,EAAIxF,gBAAkB,GAAI4H,GAAef,GAAgBA,EAAe,IAAMA,EAC7G2B,EAAcJ,EAAKzN,OAAO4N,GAC9B,OAAOD,EAASE,EAChBA,EAAY7N,OAAOwN,GAAkB5G,EAAcgH,IACrD,CCzBe,SAASE,GAAiBC,GACvC,OAAO1P,OAAOkE,OAAO,CAAC,EAAGwL,EAAM,CAC7B5O,KAAM4O,EAAKxI,EACXvC,IAAK+K,EAAKtI,EACVvG,MAAO6O,EAAKxI,EAAIwI,EAAK7I,MACrBjG,OAAQ8O,EAAKtI,EAAIsI,EAAK3I,QAE1B,CCqBA,SAAS4I,GAA2BpN,EAASqN,EAAgBlL,GAC3D,OAAOkL,IAAmBxO,EAAWqO,GCzBxB,SAAyBlN,EAASmC,GAC/C,IAAI8H,EAAM9J,EAAUH,GAChBsN,EAAOzH,EAAmB7F,GAC1ByE,EAAiBwF,EAAIxF,eACrBH,EAAQgJ,EAAKhF,YACb9D,EAAS8I,EAAKjF,aACd1D,EAAI,EACJE,EAAI,EAER,GAAIJ,EAAgB,CAClBH,EAAQG,EAAeH,MACvBE,EAASC,EAAeD,OACxB,IAAI+I,EAAiB1J,KAEjB0J,IAAmBA,GAA+B,UAAbpL,KACvCwC,EAAIF,EAAeG,WACnBC,EAAIJ,EAAeK,UAEvB,CAEA,MAAO,CACLR,MAAOA,EACPE,OAAQA,EACRG,EAAGA,EAAIyH,GAAoBpM,GAC3B6E,EAAGA,EAEP,CDDwD2I,CAAgBxN,EAASmC,IAAa1B,EAAU4M,GAdxG,SAAoCrN,EAASmC,GAC3C,IAAIgL,EAAOpJ,EAAsB/D,GAAS,EAAoB,UAAbmC,GASjD,OARAgL,EAAK/K,IAAM+K,EAAK/K,IAAMpC,EAAQyN,UAC9BN,EAAK5O,KAAO4O,EAAK5O,KAAOyB,EAAQ0N,WAChCP,EAAK9O,OAAS8O,EAAK/K,IAAMpC,EAAQqI,aACjC8E,EAAK7O,MAAQ6O,EAAK5O,KAAOyB,EAAQsI,YACjC6E,EAAK7I,MAAQtE,EAAQsI,YACrB6E,EAAK3I,OAASxE,EAAQqI,aACtB8E,EAAKxI,EAAIwI,EAAK5O,KACd4O,EAAKtI,EAAIsI,EAAK/K,IACP+K,CACT,CAG0HQ,CAA2BN,EAAgBlL,GAAY+K,GEtBlK,SAAyBlN,GACtC,IAAI8M,EAEAQ,EAAOzH,EAAmB7F,GAC1B4N,EAAY7B,GAAgB/L,GAC5B2M,EAA0D,OAAlDG,EAAwB9M,EAAQO,oBAAyB,EAASuM,EAAsBH,KAChGrI,EAAQ,EAAIgJ,EAAKO,YAAaP,EAAKhF,YAAaqE,EAAOA,EAAKkB,YAAc,EAAGlB,EAAOA,EAAKrE,YAAc,GACvG9D,EAAS,EAAI8I,EAAKQ,aAAcR,EAAKjF,aAAcsE,EAAOA,EAAKmB,aAAe,EAAGnB,EAAOA,EAAKtE,aAAe,GAC5G1D,GAAKiJ,EAAU5B,WAAaI,GAAoBpM,GAChD6E,GAAK+I,EAAU1B,UAMnB,MAJiD,QAA7C,EAAiBS,GAAQW,GAAMS,YACjCpJ,GAAK,EAAI2I,EAAKhF,YAAaqE,EAAOA,EAAKrE,YAAc,GAAKhE,GAGrD,CACLA,MAAOA,EACPE,OAAQA,EACRG,EAAGA,EACHE,EAAGA,EAEP,CFCkMmJ,CAAgBnI,EAAmB7F,IACrO,CG1Be,SAASiO,GAAe9M,GACrC,IAOIkI,EAPAtK,EAAYoC,EAAKpC,UACjBiB,EAAUmB,EAAKnB,QACfb,EAAYgC,EAAKhC,UACjBqI,EAAgBrI,EAAYuD,EAAiBvD,GAAa,KAC1DiK,EAAYjK,EAAY4J,EAAa5J,GAAa,KAClD+O,EAAUnP,EAAU4F,EAAI5F,EAAUuF,MAAQ,EAAItE,EAAQsE,MAAQ,EAC9D6J,EAAUpP,EAAU8F,EAAI9F,EAAUyF,OAAS,EAAIxE,EAAQwE,OAAS,EAGpE,OAAQgD,GACN,KAAK,EACH6B,EAAU,CACR1E,EAAGuJ,EACHrJ,EAAG9F,EAAU8F,EAAI7E,EAAQwE,QAE3B,MAEF,KAAKnG,EACHgL,EAAU,CACR1E,EAAGuJ,EACHrJ,EAAG9F,EAAU8F,EAAI9F,EAAUyF,QAE7B,MAEF,KAAKlG,EACH+K,EAAU,CACR1E,EAAG5F,EAAU4F,EAAI5F,EAAUuF,MAC3BO,EAAGsJ,GAEL,MAEF,KAAK5P,EACH8K,EAAU,CACR1E,EAAG5F,EAAU4F,EAAI3E,EAAQsE,MACzBO,EAAGsJ,GAEL,MAEF,QACE9E,EAAU,CACR1E,EAAG5F,EAAU4F,EACbE,EAAG9F,EAAU8F,GAInB,IAAIuJ,EAAW5G,EAAgBV,EAAyBU,GAAiB,KAEzE,GAAgB,MAAZ4G,EAAkB,CACpB,IAAI1G,EAAmB,MAAb0G,EAAmB,SAAW,QAExC,OAAQhF,GACN,KAAK1K,EACH2K,EAAQ+E,GAAY/E,EAAQ+E,IAAarP,EAAU2I,GAAO,EAAI1H,EAAQ0H,GAAO,GAC7E,MAEF,KAAK/I,EACH0K,EAAQ+E,GAAY/E,EAAQ+E,IAAarP,EAAU2I,GAAO,EAAI1H,EAAQ0H,GAAO,GAKnF,CAEA,OAAO2B,CACT,CC3De,SAASgF,GAAejN,EAAOc,QAC5B,IAAZA,IACFA,EAAU,CAAC,GAGb,IAAIoM,EAAWpM,EACXqM,EAAqBD,EAASnP,UAC9BA,OAAmC,IAAvBoP,EAAgCnN,EAAMjC,UAAYoP,EAC9DC,EAAoBF,EAASnM,SAC7BA,OAAiC,IAAtBqM,EAA+BpN,EAAMe,SAAWqM,EAC3DC,EAAoBH,EAASI,SAC7BA,OAAiC,IAAtBD,EAA+B7P,EAAkB6P,EAC5DE,EAAwBL,EAASM,aACjCA,OAAyC,IAA1BD,EAAmC9P,EAAW8P,EAC7DE,EAAwBP,EAASQ,eACjCA,OAA2C,IAA1BD,EAAmC/P,EAAS+P,EAC7DE,EAAuBT,EAASU,YAChCA,OAAuC,IAAzBD,GAA0CA,EACxDE,EAAmBX,EAAS3G,QAC5BA,OAA+B,IAArBsH,EAA8B,EAAIA,EAC5ChI,EAAgBD,EAAsC,iBAAZW,EAAuBA,EAAUT,EAAgBS,EAASlJ,IACpGyQ,EAAaJ,IAAmBhQ,EAASC,EAAYD,EACrDqK,EAAa/H,EAAMwG,MAAM9I,OACzBkB,EAAUoB,EAAME,SAAS0N,EAAcE,EAAaJ,GACpDK,EJkBS,SAAyBnP,EAAS0O,EAAUE,EAAczM,GACvE,IAAIiN,EAAmC,oBAAbV,EAlB5B,SAA4B1O,GAC1B,IAAIpB,EAAkBgO,GAAkB5G,EAAchG,IAElDqP,EADoB,CAAC,WAAY,SAASzJ,QAAQ,EAAiB5F,GAASiC,WAAa,GACnDtB,EAAcX,GAAWoG,EAAgBpG,GAAWA,EAE9F,OAAKS,EAAU4O,GAKRzQ,EAAgBgI,QAAO,SAAUyG,GACtC,OAAO5M,EAAU4M,IAAmBpI,EAASoI,EAAgBgC,IAAmD,SAAhCtP,EAAYsN,EAC9F,IANS,EAOX,CAK6DiC,CAAmBtP,GAAW,GAAGZ,OAAOsP,GAC/F9P,EAAkB,GAAGQ,OAAOgQ,EAAqB,CAACR,IAClDW,EAAsB3Q,EAAgB,GACtC4Q,EAAe5Q,EAAgBK,QAAO,SAAUwQ,EAASpC,GAC3D,IAAIF,EAAOC,GAA2BpN,EAASqN,EAAgBlL,GAK/D,OAJAsN,EAAQrN,IAAM,EAAI+K,EAAK/K,IAAKqN,EAAQrN,KACpCqN,EAAQnR,MAAQ,EAAI6O,EAAK7O,MAAOmR,EAAQnR,OACxCmR,EAAQpR,OAAS,EAAI8O,EAAK9O,OAAQoR,EAAQpR,QAC1CoR,EAAQlR,KAAO,EAAI4O,EAAK5O,KAAMkR,EAAQlR,MAC/BkR,CACT,GAAGrC,GAA2BpN,EAASuP,EAAqBpN,IAK5D,OAJAqN,EAAalL,MAAQkL,EAAalR,MAAQkR,EAAajR,KACvDiR,EAAahL,OAASgL,EAAanR,OAASmR,EAAapN,IACzDoN,EAAa7K,EAAI6K,EAAajR,KAC9BiR,EAAa3K,EAAI2K,EAAapN,IACvBoN,CACT,CInC2BE,CAAgBjP,EAAUT,GAAWA,EAAUA,EAAQ2P,gBAAkB9J,EAAmBzE,EAAME,SAASxC,QAAS4P,EAAUE,EAAczM,GACjKyN,EAAsB7L,EAAsB3C,EAAME,SAASvC,WAC3DuI,EAAgB2G,GAAe,CACjClP,UAAW6Q,EACX5P,QAASmJ,EACThH,SAAU,WACVhD,UAAWA,IAET0Q,EAAmB3C,GAAiBzP,OAAOkE,OAAO,CAAC,EAAGwH,EAAY7B,IAClEwI,EAAoBhB,IAAmBhQ,EAAS+Q,EAAmBD,EAGnEG,EAAkB,CACpB3N,IAAK+M,EAAmB/M,IAAM0N,EAAkB1N,IAAM6E,EAAc7E,IACpE/D,OAAQyR,EAAkBzR,OAAS8Q,EAAmB9Q,OAAS4I,EAAc5I,OAC7EE,KAAM4Q,EAAmB5Q,KAAOuR,EAAkBvR,KAAO0I,EAAc1I,KACvED,MAAOwR,EAAkBxR,MAAQ6Q,EAAmB7Q,MAAQ2I,EAAc3I,OAExE0R,EAAa5O,EAAMmG,cAAckB,OAErC,GAAIqG,IAAmBhQ,GAAUkR,EAAY,CAC3C,IAAIvH,EAASuH,EAAW7Q,GACxB1B,OAAO4D,KAAK0O,GAAiBxO,SAAQ,SAAUhE,GAC7C,IAAI0S,EAAW,CAAC3R,EAAOD,GAAQuH,QAAQrI,IAAQ,EAAI,GAAK,EACpDkK,EAAO,CAAC,EAAKpJ,GAAQuH,QAAQrI,IAAQ,EAAI,IAAM,IACnDwS,EAAgBxS,IAAQkL,EAAOhB,GAAQwI,CACzC,GACF,CAEA,OAAOF,CACT,CCyEA,UACEhP,KAAM,OACNC,SAAS,EACTC,MAAO,OACPC,GA5HF,SAAcC,GACZ,IAAIC,EAAQD,EAAKC,MACbc,EAAUf,EAAKe,QACfnB,EAAOI,EAAKJ,KAEhB,IAAIK,EAAMmG,cAAcxG,GAAMmP,MAA9B,CAoCA,IAhCA,IAAIC,EAAoBjO,EAAQkM,SAC5BgC,OAAsC,IAAtBD,GAAsCA,EACtDE,EAAmBnO,EAAQoO,QAC3BC,OAAoC,IAArBF,GAAqCA,EACpDG,EAA8BtO,EAAQuO,mBACtC9I,EAAUzF,EAAQyF,QAClB+G,EAAWxM,EAAQwM,SACnBE,EAAe1M,EAAQ0M,aACvBI,EAAc9M,EAAQ8M,YACtB0B,EAAwBxO,EAAQyO,eAChCA,OAA2C,IAA1BD,GAA0CA,EAC3DE,EAAwB1O,EAAQ0O,sBAChCC,EAAqBzP,EAAMc,QAAQ/C,UACnCqI,EAAgB9E,EAAiBmO,GAEjCJ,EAAqBD,IADHhJ,IAAkBqJ,GACqCF,EAjC/E,SAAuCxR,GACrC,GAAIuD,EAAiBvD,KAAeX,EAClC,MAAO,GAGT,IAAIsS,EAAoBnF,GAAqBxM,GAC7C,MAAO,CAAC2M,GAA8B3M,GAAY2R,EAAmBhF,GAA8BgF,GACrG,CA0B6IC,CAA8BF,GAA3E,CAAClF,GAAqBkF,KAChHG,EAAa,CAACH,GAAoBzR,OAAOqR,GAAoBxR,QAAO,SAAUC,EAAKC,GACrF,OAAOD,EAAIE,OAAOsD,EAAiBvD,KAAeX,ECvCvC,SAA8B4C,EAAOc,QAClC,IAAZA,IACFA,EAAU,CAAC,GAGb,IAAIoM,EAAWpM,EACX/C,EAAYmP,EAASnP,UACrBuP,EAAWJ,EAASI,SACpBE,EAAeN,EAASM,aACxBjH,EAAU2G,EAAS3G,QACnBgJ,EAAiBrC,EAASqC,eAC1BM,EAAwB3C,EAASsC,sBACjCA,OAAkD,IAA1BK,EAAmC,EAAgBA,EAC3E7H,EAAYL,EAAa5J,GACzB6R,EAAa5H,EAAYuH,EAAiB3R,EAAsBA,EAAoB4H,QAAO,SAAUzH,GACvG,OAAO4J,EAAa5J,KAAeiK,CACrC,IAAK3K,EACDyS,EAAoBF,EAAWpK,QAAO,SAAUzH,GAClD,OAAOyR,EAAsBhL,QAAQzG,IAAc,CACrD,IAEiC,IAA7B+R,EAAkBC,SACpBD,EAAoBF,GAItB,IAAII,EAAYF,EAAkBjS,QAAO,SAAUC,EAAKC,GAOtD,OANAD,EAAIC,GAAakP,GAAejN,EAAO,CACrCjC,UAAWA,EACXuP,SAAUA,EACVE,aAAcA,EACdjH,QAASA,IACRjF,EAAiBvD,IACbD,CACT,GAAG,CAAC,GACJ,OAAOzB,OAAO4D,KAAK+P,GAAWC,MAAK,SAAUC,EAAGC,GAC9C,OAAOH,EAAUE,GAAKF,EAAUG,EAClC,GACF,CDC6DC,CAAqBpQ,EAAO,CACnFjC,UAAWA,EACXuP,SAAUA,EACVE,aAAcA,EACdjH,QAASA,EACTgJ,eAAgBA,EAChBC,sBAAuBA,IACpBzR,EACP,GAAG,IACCsS,EAAgBrQ,EAAMwG,MAAM7I,UAC5BoK,EAAa/H,EAAMwG,MAAM9I,OACzB4S,EAAY,IAAIC,IAChBC,GAAqB,EACrBC,EAAwBb,EAAW,GAE9Bc,EAAI,EAAGA,EAAId,EAAWG,OAAQW,IAAK,CAC1C,IAAI3S,EAAY6R,EAAWc,GAEvBC,EAAiBrP,EAAiBvD,GAElC6S,EAAmBjJ,EAAa5J,KAAeT,EAC/CuT,EAAa,CAAC,EAAK5T,GAAQuH,QAAQmM,IAAmB,EACtDrK,EAAMuK,EAAa,QAAU,SAC7B1F,EAAW8B,GAAejN,EAAO,CACnCjC,UAAWA,EACXuP,SAAUA,EACVE,aAAcA,EACdI,YAAaA,EACbrH,QAASA,IAEPuK,EAAoBD,EAAaD,EAAmB1T,EAAQC,EAAOyT,EAAmB3T,EAAS,EAE/FoT,EAAc/J,GAAOyB,EAAWzB,KAClCwK,EAAoBvG,GAAqBuG,IAG3C,IAAIC,EAAmBxG,GAAqBuG,GACxCE,EAAS,GAUb,GARIhC,GACFgC,EAAOC,KAAK9F,EAASwF,IAAmB,GAGtCxB,GACF6B,EAAOC,KAAK9F,EAAS2F,IAAsB,EAAG3F,EAAS4F,IAAqB,GAG1EC,EAAOE,OAAM,SAAUC,GACzB,OAAOA,CACT,IAAI,CACFV,EAAwB1S,EACxByS,GAAqB,EACrB,KACF,CAEAF,EAAUc,IAAIrT,EAAWiT,EAC3B,CAEA,GAAIR,EAqBF,IAnBA,IAEIa,EAAQ,SAAeC,GACzB,IAAIC,EAAmB3B,EAAW4B,MAAK,SAAUzT,GAC/C,IAAIiT,EAASV,EAAU9T,IAAIuB,GAE3B,GAAIiT,EACF,OAAOA,EAAOS,MAAM,EAAGH,GAAIJ,OAAM,SAAUC,GACzC,OAAOA,CACT,GAEJ,IAEA,GAAII,EAEF,OADAd,EAAwBc,EACjB,OAEX,EAESD,EAnBY/B,EAAiB,EAAI,EAmBZ+B,EAAK,GAGpB,UAFFD,EAAMC,GADmBA,KAOpCtR,EAAMjC,YAAc0S,IACtBzQ,EAAMmG,cAAcxG,GAAMmP,OAAQ,EAClC9O,EAAMjC,UAAY0S,EAClBzQ,EAAM0R,OAAQ,EA5GhB,CA8GF,EAQEhK,iBAAkB,CAAC,UACnBgC,KAAM,CACJoF,OAAO,IE7IX,SAAS6C,GAAexG,EAAUY,EAAM6F,GAQtC,YAPyB,IAArBA,IACFA,EAAmB,CACjBrO,EAAG,EACHE,EAAG,IAIA,CACLzC,IAAKmK,EAASnK,IAAM+K,EAAK3I,OAASwO,EAAiBnO,EACnDvG,MAAOiO,EAASjO,MAAQ6O,EAAK7I,MAAQ0O,EAAiBrO,EACtDtG,OAAQkO,EAASlO,OAAS8O,EAAK3I,OAASwO,EAAiBnO,EACzDtG,KAAMgO,EAAShO,KAAO4O,EAAK7I,MAAQ0O,EAAiBrO,EAExD,CAEA,SAASsO,GAAsB1G,GAC7B,MAAO,CAAC,EAAKjO,EAAOD,EAAQE,GAAM2U,MAAK,SAAUC,GAC/C,OAAO5G,EAAS4G,IAAS,CAC3B,GACF,CA+BA,UACEpS,KAAM,OACNC,SAAS,EACTC,MAAO,OACP6H,iBAAkB,CAAC,mBACnB5H,GAlCF,SAAcC,GACZ,IAAIC,EAAQD,EAAKC,MACbL,EAAOI,EAAKJ,KACZ0Q,EAAgBrQ,EAAMwG,MAAM7I,UAC5BoK,EAAa/H,EAAMwG,MAAM9I,OACzBkU,EAAmB5R,EAAMmG,cAAc6L,gBACvCC,EAAoBhF,GAAejN,EAAO,CAC5C0N,eAAgB,cAEdwE,EAAoBjF,GAAejN,EAAO,CAC5C4N,aAAa,IAEXuE,EAA2BR,GAAeM,EAAmB5B,GAC7D+B,EAAsBT,GAAeO,EAAmBnK,EAAY6J,GACpES,EAAoBR,GAAsBM,GAC1CG,EAAmBT,GAAsBO,GAC7CpS,EAAMmG,cAAcxG,GAAQ,CAC1BwS,yBAA0BA,EAC1BC,oBAAqBA,EACrBC,kBAAmBA,EACnBC,iBAAkBA,GAEpBtS,EAAMM,WAAW5C,OAASrB,OAAOkE,OAAO,CAAC,EAAGP,EAAMM,WAAW5C,OAAQ,CACnE,+BAAgC2U,EAChC,sBAAuBC,GAE3B,GCJA,IACE3S,KAAM,SACNC,SAAS,EACTC,MAAO,OACPwB,SAAU,CAAC,iBACXvB,GA5BF,SAAgBa,GACd,IAAIX,EAAQW,EAAMX,MACdc,EAAUH,EAAMG,QAChBnB,EAAOgB,EAAMhB,KACb4S,EAAkBzR,EAAQuG,OAC1BA,OAA6B,IAApBkL,EAA6B,CAAC,EAAG,GAAKA,EAC/C7I,EAAO,EAAW7L,QAAO,SAAUC,EAAKC,GAE1C,OADAD,EAAIC,GA5BD,SAAiCA,EAAWyI,EAAOa,GACxD,IAAIjB,EAAgB9E,EAAiBvD,GACjCyU,EAAiB,CAACrV,EAAM,GAAKqH,QAAQ4B,IAAkB,GAAK,EAAI,EAEhErG,EAAyB,mBAAXsH,EAAwBA,EAAOhL,OAAOkE,OAAO,CAAC,EAAGiG,EAAO,CACxEzI,UAAWA,KACPsJ,EACFoL,EAAW1S,EAAK,GAChB2S,EAAW3S,EAAK,GAIpB,OAFA0S,EAAWA,GAAY,EACvBC,GAAYA,GAAY,GAAKF,EACtB,CAACrV,EAAMD,GAAOsH,QAAQ4B,IAAkB,EAAI,CACjD7C,EAAGmP,EACHjP,EAAGgP,GACD,CACFlP,EAAGkP,EACHhP,EAAGiP,EAEP,CASqBC,CAAwB5U,EAAWiC,EAAMwG,MAAOa,GAC1DvJ,CACT,GAAG,CAAC,GACA8U,EAAwBlJ,EAAK1J,EAAMjC,WACnCwF,EAAIqP,EAAsBrP,EAC1BE,EAAImP,EAAsBnP,EAEW,MAArCzD,EAAMmG,cAAcD,gBACtBlG,EAAMmG,cAAcD,cAAc3C,GAAKA,EACvCvD,EAAMmG,cAAcD,cAAczC,GAAKA,GAGzCzD,EAAMmG,cAAcxG,GAAQ+J,CAC9B,GC1BA,IACE/J,KAAM,gBACNC,SAAS,EACTC,MAAO,OACPC,GApBF,SAAuBC,GACrB,IAAIC,EAAQD,EAAKC,MACbL,EAAOI,EAAKJ,KAKhBK,EAAMmG,cAAcxG,GAAQkN,GAAe,CACzClP,UAAWqC,EAAMwG,MAAM7I,UACvBiB,QAASoB,EAAMwG,MAAM9I,OACrBqD,SAAU,WACVhD,UAAWiC,EAAMjC,WAErB,EAQE2L,KAAM,CAAC,GCgHT,IACE/J,KAAM,kBACNC,SAAS,EACTC,MAAO,OACPC,GA/HF,SAAyBC,GACvB,IAAIC,EAAQD,EAAKC,MACbc,EAAUf,EAAKe,QACfnB,EAAOI,EAAKJ,KACZoP,EAAoBjO,EAAQkM,SAC5BgC,OAAsC,IAAtBD,GAAsCA,EACtDE,EAAmBnO,EAAQoO,QAC3BC,OAAoC,IAArBF,GAAsCA,EACrD3B,EAAWxM,EAAQwM,SACnBE,EAAe1M,EAAQ0M,aACvBI,EAAc9M,EAAQ8M,YACtBrH,EAAUzF,EAAQyF,QAClBsM,EAAkB/R,EAAQgS,OAC1BA,OAA6B,IAApBD,GAAoCA,EAC7CE,EAAwBjS,EAAQkS,aAChCA,OAAyC,IAA1BD,EAAmC,EAAIA,EACtD5H,EAAW8B,GAAejN,EAAO,CACnCsN,SAAUA,EACVE,aAAcA,EACdjH,QAASA,EACTqH,YAAaA,IAEXxH,EAAgB9E,EAAiBtB,EAAMjC,WACvCiK,EAAYL,EAAa3H,EAAMjC,WAC/BkV,GAAmBjL,EACnBgF,EAAWtH,EAAyBU,GACpC8I,ECrCY,MDqCSlC,ECrCH,IAAM,IDsCxB9G,EAAgBlG,EAAMmG,cAAcD,cACpCmK,EAAgBrQ,EAAMwG,MAAM7I,UAC5BoK,EAAa/H,EAAMwG,MAAM9I,OACzBwV,EAA4C,mBAAjBF,EAA8BA,EAAa3W,OAAOkE,OAAO,CAAC,EAAGP,EAAMwG,MAAO,CACvGzI,UAAWiC,EAAMjC,aACbiV,EACFG,EAA2D,iBAAtBD,EAAiC,CACxElG,SAAUkG,EACVhE,QAASgE,GACP7W,OAAOkE,OAAO,CAChByM,SAAU,EACVkC,QAAS,GACRgE,GACCE,EAAsBpT,EAAMmG,cAAckB,OAASrH,EAAMmG,cAAckB,OAAOrH,EAAMjC,WAAa,KACjG2L,EAAO,CACTnG,EAAG,EACHE,EAAG,GAGL,GAAKyC,EAAL,CAIA,GAAI8I,EAAe,CACjB,IAAIqE,EAEAC,EAAwB,MAAbtG,EAAmB,EAAM7P,EACpCoW,EAAuB,MAAbvG,EAAmB/P,EAASC,EACtCoJ,EAAmB,MAAb0G,EAAmB,SAAW,QACpC3F,EAASnB,EAAc8G,GACvBtL,EAAM2F,EAAS8D,EAASmI,GACxB7R,EAAM4F,EAAS8D,EAASoI,GACxBC,EAAWV,GAAU/K,EAAWzB,GAAO,EAAI,EAC3CmN,EAASzL,IAAc1K,EAAQ+S,EAAc/J,GAAOyB,EAAWzB,GAC/DoN,EAAS1L,IAAc1K,GAASyK,EAAWzB,IAAQ+J,EAAc/J,GAGjEL,EAAejG,EAAME,SAASgB,MAC9BwF,EAAYoM,GAAU7M,EAAetC,EAAcsC,GAAgB,CACrE/C,MAAO,EACPE,OAAQ,GAENuQ,GAAqB3T,EAAMmG,cAAc,oBAAsBnG,EAAMmG,cAAc,oBAAoBI,QxBhFtG,CACLvF,IAAK,EACL9D,MAAO,EACPD,OAAQ,EACRE,KAAM,GwB6EFyW,GAAkBD,GAAmBL,GACrCO,GAAkBF,GAAmBJ,GAMrCO,GAAWnO,EAAO,EAAG0K,EAAc/J,GAAMI,EAAUJ,IACnDyN,GAAYd,EAAkB5C,EAAc/J,GAAO,EAAIkN,EAAWM,GAAWF,GAAkBT,EAA4BnG,SAAWyG,EAASK,GAAWF,GAAkBT,EAA4BnG,SACxMgH,GAAYf,GAAmB5C,EAAc/J,GAAO,EAAIkN,EAAWM,GAAWD,GAAkBV,EAA4BnG,SAAW0G,EAASI,GAAWD,GAAkBV,EAA4BnG,SACzMjG,GAAoB/G,EAAME,SAASgB,OAAS8D,EAAgBhF,EAAME,SAASgB,OAC3E+S,GAAelN,GAAiC,MAAbiG,EAAmBjG,GAAkBsF,WAAa,EAAItF,GAAkBuF,YAAc,EAAI,EAC7H4H,GAAwH,OAAjGb,EAA+C,MAAvBD,OAA8B,EAASA,EAAoBpG,IAAqBqG,EAAwB,EAEvJc,GAAY9M,EAAS2M,GAAYE,GACjCE,GAAkBzO,EAAOmN,EAAS,EAAQpR,EAF9B2F,EAAS0M,GAAYG,GAAsBD,IAEKvS,EAAK2F,EAAQyL,EAAS,EAAQrR,EAAK0S,IAAa1S,GAChHyE,EAAc8G,GAAYoH,GAC1B1K,EAAKsD,GAAYoH,GAAkB/M,CACrC,CAEA,GAAI8H,EAAc,CAChB,IAAIkF,GAEAC,GAAyB,MAAbtH,EAAmB,EAAM7P,EAErCoX,GAAwB,MAAbvH,EAAmB/P,EAASC,EAEvCsX,GAAUtO,EAAcgJ,GAExBuF,GAAmB,MAAZvF,EAAkB,SAAW,QAEpCwF,GAAOF,GAAUrJ,EAASmJ,IAE1BK,GAAOH,GAAUrJ,EAASoJ,IAE1BK,IAAuD,IAAxC,CAAC,EAAKzX,GAAMqH,QAAQ4B,GAEnCyO,GAAyH,OAAjGR,GAAgD,MAAvBjB,OAA8B,EAASA,EAAoBlE,IAAoBmF,GAAyB,EAEzJS,GAAaF,GAAeF,GAAOF,GAAUnE,EAAcoE,IAAQ1M,EAAW0M,IAAQI,GAAuB1B,EAA4BjE,QAEzI6F,GAAaH,GAAeJ,GAAUnE,EAAcoE,IAAQ1M,EAAW0M,IAAQI,GAAuB1B,EAA4BjE,QAAUyF,GAE5IK,GAAmBlC,GAAU8B,G1BzH9B,SAAwBlT,EAAK1E,EAAOyE,GACzC,IAAIwT,EAAItP,EAAOjE,EAAK1E,EAAOyE,GAC3B,OAAOwT,EAAIxT,EAAMA,EAAMwT,CACzB,C0BsHoDC,CAAeJ,GAAYN,GAASO,IAAcpP,EAAOmN,EAASgC,GAAaJ,GAAMF,GAAS1B,EAASiC,GAAaJ,IAEpKzO,EAAcgJ,GAAW8F,GACzBtL,EAAKwF,GAAW8F,GAAmBR,EACrC,CAEAxU,EAAMmG,cAAcxG,GAAQ+J,CAvE5B,CAwEF,EAQEhC,iBAAkB,CAAC,WE1HN,SAASyN,GAAiBC,EAAyBrQ,EAAcsD,QAC9D,IAAZA,IACFA,GAAU,GAGZ,ICnBoCrJ,ECJOJ,EFuBvCyW,EAA0B9V,EAAcwF,GACxCuQ,EAAuB/V,EAAcwF,IAf3C,SAAyBnG,GACvB,IAAImN,EAAOnN,EAAQ+D,wBACfI,EAASpB,EAAMoK,EAAK7I,OAAStE,EAAQqE,aAAe,EACpDD,EAASrB,EAAMoK,EAAK3I,QAAUxE,EAAQuE,cAAgB,EAC1D,OAAkB,IAAXJ,GAA2B,IAAXC,CACzB,CAU4DuS,CAAgBxQ,GACtEJ,EAAkBF,EAAmBM,GACrCgH,EAAOpJ,EAAsByS,EAAyBE,EAAsBjN,GAC5EyB,EAAS,CACXc,WAAY,EACZE,UAAW,GAET7C,EAAU,CACZ1E,EAAG,EACHE,EAAG,GAkBL,OAfI4R,IAA4BA,IAA4BhN,MACxB,SAA9B1J,EAAYoG,IAChBkG,GAAetG,MACbmF,GCnCgC9K,EDmCT+F,KClCdhG,EAAUC,IAAUO,EAAcP,GCJxC,CACL4L,YAFyChM,EDQbI,GCNR4L,WACpBE,UAAWlM,EAAQkM,WDGZH,GAAgB3L,IDoCnBO,EAAcwF,KAChBkD,EAAUtF,EAAsBoC,GAAc,IACtCxB,GAAKwB,EAAauH,WAC1BrE,EAAQxE,GAAKsB,EAAasH,WACjB1H,IACTsD,EAAQ1E,EAAIyH,GAAoBrG,KAI7B,CACLpB,EAAGwI,EAAK5O,KAAO2M,EAAOc,WAAa3C,EAAQ1E,EAC3CE,EAAGsI,EAAK/K,IAAM8I,EAAOgB,UAAY7C,EAAQxE,EACzCP,MAAO6I,EAAK7I,MACZE,OAAQ2I,EAAK3I,OAEjB,CGvDA,SAASoS,GAAMC,GACb,IAAItT,EAAM,IAAIoO,IACVmF,EAAU,IAAIC,IACdC,EAAS,GAKb,SAAS3F,EAAK4F,GACZH,EAAQI,IAAID,EAASlW,MACN,GAAG3B,OAAO6X,EAASxU,UAAY,GAAIwU,EAASnO,kBAAoB,IACtEvH,SAAQ,SAAU4V,GACzB,IAAKL,EAAQM,IAAID,GAAM,CACrB,IAAIE,EAAc9T,EAAI3F,IAAIuZ,GAEtBE,GACFhG,EAAKgG,EAET,CACF,IACAL,EAAO3E,KAAK4E,EACd,CAQA,OAzBAJ,EAAUtV,SAAQ,SAAU0V,GAC1B1T,EAAIiP,IAAIyE,EAASlW,KAAMkW,EACzB,IAiBAJ,EAAUtV,SAAQ,SAAU0V,GACrBH,EAAQM,IAAIH,EAASlW,OAExBsQ,EAAK4F,EAET,IACOD,CACT,CCvBA,IAAIM,GAAkB,CACpBnY,UAAW,SACX0X,UAAW,GACX1U,SAAU,YAGZ,SAASoV,KACP,IAAK,IAAI1B,EAAO2B,UAAUrG,OAAQsG,EAAO,IAAIpU,MAAMwS,GAAO6B,EAAO,EAAGA,EAAO7B,EAAM6B,IAC/ED,EAAKC,GAAQF,UAAUE,GAGzB,OAAQD,EAAKvE,MAAK,SAAUlT,GAC1B,QAASA,GAAoD,mBAAlCA,EAAQ+D,sBACrC,GACF,CAEO,SAAS4T,GAAgBC,QACL,IAArBA,IACFA,EAAmB,CAAC,GAGtB,IAAIC,EAAoBD,EACpBE,EAAwBD,EAAkBE,iBAC1CA,OAA6C,IAA1BD,EAAmC,GAAKA,EAC3DE,EAAyBH,EAAkBI,eAC3CA,OAA4C,IAA3BD,EAAoCV,GAAkBU,EAC3E,OAAO,SAAsBjZ,EAAWD,EAAQoD,QAC9B,IAAZA,IACFA,EAAU+V,GAGZ,ICxC6B/W,EAC3BgX,EDuCE9W,EAAQ,CACVjC,UAAW,SACXgZ,iBAAkB,GAClBjW,QAASzE,OAAOkE,OAAO,CAAC,EAAG2V,GAAiBW,GAC5C1Q,cAAe,CAAC,EAChBjG,SAAU,CACRvC,UAAWA,EACXD,OAAQA,GAEV4C,WAAY,CAAC,EACbD,OAAQ,CAAC,GAEP2W,EAAmB,GACnBC,GAAc,EACdrN,EAAW,CACb5J,MAAOA,EACPkX,WAAY,SAAoBC,GAC9B,IAAIrW,EAAsC,mBAArBqW,EAAkCA,EAAiBnX,EAAMc,SAAWqW,EACzFC,IACApX,EAAMc,QAAUzE,OAAOkE,OAAO,CAAC,EAAGsW,EAAgB7W,EAAMc,QAASA,GACjEd,EAAMiK,cAAgB,CACpBtM,UAAW0B,EAAU1B,GAAa6N,GAAkB7N,GAAaA,EAAU4Q,eAAiB/C,GAAkB7N,EAAU4Q,gBAAkB,GAC1I7Q,OAAQ8N,GAAkB9N,IAI5B,IElE4B+X,EAC9B4B,EFiEMN,EDhCG,SAAwBtB,GAErC,IAAIsB,EAAmBvB,GAAMC,GAE7B,OAAO/W,EAAeb,QAAO,SAAUC,EAAK+B,GAC1C,OAAO/B,EAAIE,OAAO+Y,EAAiBvR,QAAO,SAAUqQ,GAClD,OAAOA,EAAShW,QAAUA,CAC5B,IACF,GAAG,GACL,CCuB+ByX,EElEK7B,EFkEsB,GAAGzX,OAAO2Y,EAAkB3W,EAAMc,QAAQ2U,WEjE9F4B,EAAS5B,EAAU5X,QAAO,SAAUwZ,EAAQE,GAC9C,IAAIC,EAAWH,EAAOE,EAAQ5X,MAK9B,OAJA0X,EAAOE,EAAQ5X,MAAQ6X,EAAWnb,OAAOkE,OAAO,CAAC,EAAGiX,EAAUD,EAAS,CACrEzW,QAASzE,OAAOkE,OAAO,CAAC,EAAGiX,EAAS1W,QAASyW,EAAQzW,SACrD4I,KAAMrN,OAAOkE,OAAO,CAAC,EAAGiX,EAAS9N,KAAM6N,EAAQ7N,QAC5C6N,EACEF,CACT,GAAG,CAAC,GAEGhb,OAAO4D,KAAKoX,GAAQlV,KAAI,SAAUhG,GACvC,OAAOkb,EAAOlb,EAChB,MF4DM,OAJA6D,EAAM+W,iBAAmBA,EAAiBvR,QAAO,SAAUiS,GACzD,OAAOA,EAAE7X,OACX,IA+FFI,EAAM+W,iBAAiB5W,SAAQ,SAAUJ,GACvC,IAAIJ,EAAOI,EAAKJ,KACZ+X,EAAe3X,EAAKe,QACpBA,OAA2B,IAAjB4W,EAA0B,CAAC,EAAIA,EACzChX,EAASX,EAAKW,OAElB,GAAsB,mBAAXA,EAAuB,CAChC,IAAIiX,EAAYjX,EAAO,CACrBV,MAAOA,EACPL,KAAMA,EACNiK,SAAUA,EACV9I,QAASA,IAKXkW,EAAiB/F,KAAK0G,GAFT,WAAmB,EAGlC,CACF,IA/GS/N,EAASQ,QAClB,EAMAwN,YAAa,WACX,IAAIX,EAAJ,CAIA,IAAIY,EAAkB7X,EAAME,SACxBvC,EAAYka,EAAgBla,UAC5BD,EAASma,EAAgBna,OAG7B,GAAKyY,GAAiBxY,EAAWD,GAAjC,CAKAsC,EAAMwG,MAAQ,CACZ7I,UAAWwX,GAAiBxX,EAAWqH,EAAgBtH,GAAoC,UAA3BsC,EAAMc,QAAQC,UAC9ErD,OAAQiG,EAAcjG,IAOxBsC,EAAM0R,OAAQ,EACd1R,EAAMjC,UAAYiC,EAAMc,QAAQ/C,UAKhCiC,EAAM+W,iBAAiB5W,SAAQ,SAAU0V,GACvC,OAAO7V,EAAMmG,cAAc0P,EAASlW,MAAQtD,OAAOkE,OAAO,CAAC,EAAGsV,EAASnM,KACzE,IAEA,IAAK,IAAIoO,EAAQ,EAAGA,EAAQ9X,EAAM+W,iBAAiBhH,OAAQ+H,IACzD,IAAoB,IAAhB9X,EAAM0R,MAAV,CAMA,IAAIqG,EAAwB/X,EAAM+W,iBAAiBe,GAC/ChY,EAAKiY,EAAsBjY,GAC3BkY,EAAyBD,EAAsBjX,QAC/CoM,OAAsC,IAA3B8K,EAAoC,CAAC,EAAIA,EACpDrY,EAAOoY,EAAsBpY,KAEf,mBAAPG,IACTE,EAAQF,EAAG,CACTE,MAAOA,EACPc,QAASoM,EACTvN,KAAMA,EACNiK,SAAUA,KACN5J,EAdR,MAHEA,EAAM0R,OAAQ,EACdoG,GAAS,CAzBb,CATA,CAqDF,EAGA1N,QC1I2BtK,ED0IV,WACf,OAAO,IAAImY,SAAQ,SAAUC,GAC3BtO,EAASgO,cACTM,EAAQlY,EACV,GACF,EC7IG,WAUL,OATK8W,IACHA,EAAU,IAAImB,SAAQ,SAAUC,GAC9BD,QAAQC,UAAUC,MAAK,WACrBrB,OAAUsB,EACVF,EAAQpY,IACV,GACF,KAGKgX,CACT,GDmIIuB,QAAS,WACPjB,IACAH,GAAc,CAChB,GAGF,IAAKd,GAAiBxY,EAAWD,GAC/B,OAAOkM,EAmCT,SAASwN,IACPJ,EAAiB7W,SAAQ,SAAUL,GACjC,OAAOA,GACT,IACAkX,EAAmB,EACrB,CAEA,OAvCApN,EAASsN,WAAWpW,GAASqX,MAAK,SAAUnY,IACrCiX,GAAenW,EAAQwX,eAC1BxX,EAAQwX,cAActY,EAE1B,IAmCO4J,CACT,CACF,CACO,IAAI2O,GAA4BhC,KGzLnC,GAA4BA,GAAgB,CAC9CI,iBAFqB,CAAC6B,GAAgB,GAAe,GAAe,EAAa,GAAQ,GAAM,GAAiB,EAAO,MCJrH,GAA4BjC,GAAgB,CAC9CI,iBAFqB,CAAC6B,GAAgB,GAAe,GAAe,KCatE,MAAMC,GAAa,IAAIlI,IACjBmI,GAAO,CACX,GAAAtH,CAAIxS,EAASzC,EAAKyN,GACX6O,GAAWzC,IAAIpX,IAClB6Z,GAAWrH,IAAIxS,EAAS,IAAI2R,KAE9B,MAAMoI,EAAcF,GAAWjc,IAAIoC,GAI9B+Z,EAAY3C,IAAI7Z,IAA6B,IAArBwc,EAAYC,KAKzCD,EAAYvH,IAAIjV,EAAKyN,GAHnBiP,QAAQC,MAAM,+EAA+E7W,MAAM8W,KAAKJ,EAAY1Y,QAAQ,MAIhI,EACAzD,IAAG,CAACoC,EAASzC,IACPsc,GAAWzC,IAAIpX,IACV6Z,GAAWjc,IAAIoC,GAASpC,IAAIL,IAE9B,KAET,MAAA6c,CAAOpa,EAASzC,GACd,IAAKsc,GAAWzC,IAAIpX,GAClB,OAEF,MAAM+Z,EAAcF,GAAWjc,IAAIoC,GACnC+Z,EAAYM,OAAO9c,GAGM,IAArBwc,EAAYC,MACdH,GAAWQ,OAAOra,EAEtB,GAYIsa,GAAiB,gBAOjBC,GAAgBC,IAChBA,GAAYna,OAAOoa,KAAOpa,OAAOoa,IAAIC,SAEvCF,EAAWA,EAAS5O,QAAQ,iBAAiB,CAAC+O,EAAOC,IAAO,IAAIH,IAAIC,OAAOE,QAEtEJ,GA4CHK,GAAuB7a,IAC3BA,EAAQ8a,cAAc,IAAIC,MAAMT,IAAgB,EAE5C,GAAYU,MACXA,GAA4B,iBAAXA,UAGO,IAAlBA,EAAOC,SAChBD,EAASA,EAAO,SAEgB,IAApBA,EAAOE,UAEjBC,GAAaH,GAEb,GAAUA,GACLA,EAAOC,OAASD,EAAO,GAAKA,EAEf,iBAAXA,GAAuBA,EAAO7J,OAAS,EACzCrL,SAAS+C,cAAc0R,GAAcS,IAEvC,KAEHI,GAAYpb,IAChB,IAAK,GAAUA,IAAgD,IAApCA,EAAQqb,iBAAiBlK,OAClD,OAAO,EAET,MAAMmK,EAAgF,YAA7D5V,iBAAiB1F,GAASub,iBAAiB,cAE9DC,EAAgBxb,EAAQyb,QAAQ,uBACtC,IAAKD,EACH,OAAOF,EAET,GAAIE,IAAkBxb,EAAS,CAC7B,MAAM0b,EAAU1b,EAAQyb,QAAQ,WAChC,GAAIC,GAAWA,EAAQlW,aAAegW,EACpC,OAAO,EAET,GAAgB,OAAZE,EACF,OAAO,CAEX,CACA,OAAOJ,CAAgB,EAEnBK,GAAa3b,IACZA,GAAWA,EAAQkb,WAAaU,KAAKC,gBAGtC7b,EAAQ8b,UAAU7W,SAAS,mBAGC,IAArBjF,EAAQ+b,SACV/b,EAAQ+b,SAEV/b,EAAQgc,aAAa,aAAoD,UAArChc,EAAQic,aAAa,aAE5DC,GAAiBlc,IACrB,IAAK8F,SAASC,gBAAgBoW,aAC5B,OAAO,KAIT,GAAmC,mBAAxBnc,EAAQqF,YAA4B,CAC7C,MAAM+W,EAAOpc,EAAQqF,cACrB,OAAO+W,aAAgBtb,WAAasb,EAAO,IAC7C,CACA,OAAIpc,aAAmBc,WACdd,EAIJA,EAAQwF,WAGN0W,GAAelc,EAAQwF,YAFrB,IAEgC,EAErC6W,GAAO,OAUPC,GAAStc,IACbA,EAAQuE,YAAY,EAEhBgY,GAAY,IACZlc,OAAOmc,SAAW1W,SAAS6G,KAAKqP,aAAa,qBACxC3b,OAAOmc,OAET,KAEHC,GAA4B,GAgB5BC,GAAQ,IAAuC,QAAjC5W,SAASC,gBAAgB4W,IACvCC,GAAqBC,IAhBAC,QAiBN,KACjB,MAAMC,EAAIR,KAEV,GAAIQ,EAAG,CACL,MAAMhc,EAAO8b,EAAOG,KACdC,EAAqBF,EAAE7b,GAAGH,GAChCgc,EAAE7b,GAAGH,GAAQ8b,EAAOK,gBACpBH,EAAE7b,GAAGH,GAAMoc,YAAcN,EACzBE,EAAE7b,GAAGH,GAAMqc,WAAa,KACtBL,EAAE7b,GAAGH,GAAQkc,EACNJ,EAAOK,gBAElB,GA5B0B,YAAxBpX,SAASuX,YAENZ,GAA0BtL,QAC7BrL,SAASyF,iBAAiB,oBAAoB,KAC5C,IAAK,MAAMuR,KAAYL,GACrBK,GACF,IAGJL,GAA0BpK,KAAKyK,IAE/BA,GAkBA,EAEEQ,GAAU,CAACC,EAAkB9F,EAAO,GAAI+F,EAAeD,IACxB,mBAArBA,EAAkCA,KAAoB9F,GAAQ+F,EAExEC,GAAyB,CAACX,EAAUY,EAAmBC,GAAoB,KAC/E,IAAKA,EAEH,YADAL,GAAQR,GAGV,MACMc,EA/JiC5d,KACvC,IAAKA,EACH,OAAO,EAIT,IAAI,mBACF6d,EAAkB,gBAClBC,GACEzd,OAAOqF,iBAAiB1F,GAC5B,MAAM+d,EAA0BC,OAAOC,WAAWJ,GAC5CK,EAAuBF,OAAOC,WAAWH,GAG/C,OAAKC,GAA4BG,GAKjCL,EAAqBA,EAAmBlb,MAAM,KAAK,GACnDmb,EAAkBA,EAAgBnb,MAAM,KAAK,GAtDf,KAuDtBqb,OAAOC,WAAWJ,GAAsBG,OAAOC,WAAWH,KANzD,CAMoG,EA0IpFK,CAAiCT,GADlC,EAExB,IAAIU,GAAS,EACb,MAAMC,EAAU,EACdrR,aAEIA,IAAW0Q,IAGfU,GAAS,EACTV,EAAkBjS,oBAAoB6O,GAAgB+D,GACtDf,GAAQR,GAAS,EAEnBY,EAAkBnS,iBAAiB+O,GAAgB+D,GACnDC,YAAW,KACJF,GACHvD,GAAqB6C,EACvB,GACCE,EAAiB,EAYhBW,GAAuB,CAAC1R,EAAM2R,EAAeC,EAAeC,KAChE,MAAMC,EAAa9R,EAAKsE,OACxB,IAAI+H,EAAQrM,EAAKjH,QAAQ4Y,GAIzB,OAAe,IAAXtF,GACMuF,GAAiBC,EAAiB7R,EAAK8R,EAAa,GAAK9R,EAAK,IAExEqM,GAASuF,EAAgB,GAAK,EAC1BC,IACFxF,GAASA,EAAQyF,GAAcA,GAE1B9R,EAAKjK,KAAKC,IAAI,EAAGD,KAAKE,IAAIoW,EAAOyF,EAAa,KAAI,EAerDC,GAAiB,qBACjBC,GAAiB,OACjBC,GAAgB,SAChBC,GAAgB,CAAC,EACvB,IAAIC,GAAW,EACf,MAAMC,GAAe,CACnBC,WAAY,YACZC,WAAY,YAERC,GAAe,IAAIrI,IAAI,CAAC,QAAS,WAAY,UAAW,YAAa,cAAe,aAAc,iBAAkB,YAAa,WAAY,YAAa,cAAe,YAAa,UAAW,WAAY,QAAS,oBAAqB,aAAc,YAAa,WAAY,cAAe,cAAe,cAAe,YAAa,eAAgB,gBAAiB,eAAgB,gBAAiB,aAAc,QAAS,OAAQ,SAAU,QAAS,SAAU,SAAU,UAAW,WAAY,OAAQ,SAAU,eAAgB,SAAU,OAAQ,mBAAoB,mBAAoB,QAAS,QAAS,WAM/lB,SAASsI,GAAarf,EAASsf,GAC7B,OAAOA,GAAO,GAAGA,MAAQN,QAAgBhf,EAAQgf,UAAYA,IAC/D,CACA,SAASO,GAAiBvf,GACxB,MAAMsf,EAAMD,GAAarf,GAGzB,OAFAA,EAAQgf,SAAWM,EACnBP,GAAcO,GAAOP,GAAcO,IAAQ,CAAC,EACrCP,GAAcO,EACvB,CAiCA,SAASE,GAAYC,EAAQC,EAAUC,EAAqB,MAC1D,OAAOliB,OAAOmiB,OAAOH,GAAQ7M,MAAKiN,GAASA,EAAMH,WAAaA,GAAYG,EAAMF,qBAAuBA,GACzG,CACA,SAASG,GAAoBC,EAAmB1B,EAAS2B,GACvD,MAAMC,EAAiC,iBAAZ5B,EAErBqB,EAAWO,EAAcD,EAAqB3B,GAAW2B,EAC/D,IAAIE,EAAYC,GAAaJ,GAI7B,OAHKX,GAAahI,IAAI8I,KACpBA,EAAYH,GAEP,CAACE,EAAaP,EAAUQ,EACjC,CACA,SAASE,GAAWpgB,EAAS+f,EAAmB1B,EAAS2B,EAAoBK,GAC3E,GAAiC,iBAAtBN,IAAmC/f,EAC5C,OAEF,IAAKigB,EAAaP,EAAUQ,GAAaJ,GAAoBC,EAAmB1B,EAAS2B,GAIzF,GAAID,KAAqBd,GAAc,CACrC,MAAMqB,EAAepf,GACZ,SAAU2e,GACf,IAAKA,EAAMU,eAAiBV,EAAMU,gBAAkBV,EAAMW,iBAAmBX,EAAMW,eAAevb,SAAS4a,EAAMU,eAC/G,OAAOrf,EAAGjD,KAAKwiB,KAAMZ,EAEzB,EAEFH,EAAWY,EAAaZ,EAC1B,CACA,MAAMD,EAASF,GAAiBvf,GAC1B0gB,EAAWjB,EAAOS,KAAeT,EAAOS,GAAa,CAAC,GACtDS,EAAmBnB,GAAYkB,EAAUhB,EAAUO,EAAc5B,EAAU,MACjF,GAAIsC,EAEF,YADAA,EAAiBN,OAASM,EAAiBN,QAAUA,GAGvD,MAAMf,EAAMD,GAAaK,EAAUK,EAAkBnU,QAAQgT,GAAgB,KACvE1d,EAAK+e,EA5Db,SAAoCjgB,EAASwa,EAAUtZ,GACrD,OAAO,SAASmd,EAAQwB,GACtB,MAAMe,EAAc5gB,EAAQ6gB,iBAAiBrG,GAC7C,IAAK,IAAI,OACPxN,GACE6S,EAAO7S,GAAUA,IAAWyT,KAAMzT,EAASA,EAAOxH,WACpD,IAAK,MAAMsb,KAAcF,EACvB,GAAIE,IAAe9T,EASnB,OANA+T,GAAWlB,EAAO,CAChBW,eAAgBxT,IAEdqR,EAAQgC,QACVW,GAAaC,IAAIjhB,EAAS6f,EAAMqB,KAAM1G,EAAUtZ,GAE3CA,EAAGigB,MAAMnU,EAAQ,CAAC6S,GAG/B,CACF,CAwC2BuB,CAA2BphB,EAASqe,EAASqB,GAvExE,SAA0B1f,EAASkB,GACjC,OAAO,SAASmd,EAAQwB,GAOtB,OANAkB,GAAWlB,EAAO,CAChBW,eAAgBxgB,IAEdqe,EAAQgC,QACVW,GAAaC,IAAIjhB,EAAS6f,EAAMqB,KAAMhgB,GAEjCA,EAAGigB,MAAMnhB,EAAS,CAAC6f,GAC5B,CACF,CA6DoFwB,CAAiBrhB,EAAS0f,GAC5Gxe,EAAGye,mBAAqBM,EAAc5B,EAAU,KAChDnd,EAAGwe,SAAWA,EACdxe,EAAGmf,OAASA,EACZnf,EAAG8d,SAAWM,EACdoB,EAASpB,GAAOpe,EAChBlB,EAAQuL,iBAAiB2U,EAAWhf,EAAI+e,EAC1C,CACA,SAASqB,GAActhB,EAASyf,EAAQS,EAAW7B,EAASsB,GAC1D,MAAMze,EAAKse,GAAYC,EAAOS,GAAY7B,EAASsB,GAC9Cze,IAGLlB,EAAQyL,oBAAoByU,EAAWhf,EAAIqgB,QAAQ5B,WAC5CF,EAAOS,GAAWhf,EAAG8d,UAC9B,CACA,SAASwC,GAAyBxhB,EAASyf,EAAQS,EAAWuB,GAC5D,MAAMC,EAAoBjC,EAAOS,IAAc,CAAC,EAChD,IAAK,MAAOyB,EAAY9B,KAAUpiB,OAAOmkB,QAAQF,GAC3CC,EAAWE,SAASJ,IACtBH,GAActhB,EAASyf,EAAQS,EAAWL,EAAMH,SAAUG,EAAMF,mBAGtE,CACA,SAASQ,GAAaN,GAGpB,OADAA,EAAQA,EAAMjU,QAAQiT,GAAgB,IAC/BI,GAAaY,IAAUA,CAChC,CACA,MAAMmB,GAAe,CACnB,EAAAc,CAAG9hB,EAAS6f,EAAOxB,EAAS2B,GAC1BI,GAAWpgB,EAAS6f,EAAOxB,EAAS2B,GAAoB,EAC1D,EACA,GAAA+B,CAAI/hB,EAAS6f,EAAOxB,EAAS2B,GAC3BI,GAAWpgB,EAAS6f,EAAOxB,EAAS2B,GAAoB,EAC1D,EACA,GAAAiB,CAAIjhB,EAAS+f,EAAmB1B,EAAS2B,GACvC,GAAiC,iBAAtBD,IAAmC/f,EAC5C,OAEF,MAAOigB,EAAaP,EAAUQ,GAAaJ,GAAoBC,EAAmB1B,EAAS2B,GACrFgC,EAAc9B,IAAcH,EAC5BN,EAASF,GAAiBvf,GAC1B0hB,EAAoBjC,EAAOS,IAAc,CAAC,EAC1C+B,EAAclC,EAAkBmC,WAAW,KACjD,QAAwB,IAAbxC,EAAX,CAQA,GAAIuC,EACF,IAAK,MAAME,KAAgB1kB,OAAO4D,KAAKoe,GACrC+B,GAAyBxhB,EAASyf,EAAQ0C,EAAcpC,EAAkBlN,MAAM,IAGpF,IAAK,MAAOuP,EAAavC,KAAUpiB,OAAOmkB,QAAQF,GAAoB,CACpE,MAAMC,EAAaS,EAAYxW,QAAQkT,GAAe,IACjDkD,IAAejC,EAAkB8B,SAASF,IAC7CL,GAActhB,EAASyf,EAAQS,EAAWL,EAAMH,SAAUG,EAAMF,mBAEpE,CAXA,KAPA,CAEE,IAAKliB,OAAO4D,KAAKqgB,GAAmBvQ,OAClC,OAEFmQ,GAActhB,EAASyf,EAAQS,EAAWR,EAAUO,EAAc5B,EAAU,KAE9E,CAYF,EACA,OAAAgE,CAAQriB,EAAS6f,EAAOpI,GACtB,GAAqB,iBAAVoI,IAAuB7f,EAChC,OAAO,KAET,MAAM+c,EAAIR,KAGV,IAAI+F,EAAc,KACdC,GAAU,EACVC,GAAiB,EACjBC,GAAmB,EAJH5C,IADFM,GAAaN,IAMZ9C,IACjBuF,EAAcvF,EAAEhC,MAAM8E,EAAOpI,GAC7BsF,EAAE/c,GAASqiB,QAAQC,GACnBC,GAAWD,EAAYI,uBACvBF,GAAkBF,EAAYK,gCAC9BF,EAAmBH,EAAYM,sBAEjC,MAAMC,EAAM9B,GAAW,IAAIhG,MAAM8E,EAAO,CACtC0C,UACAO,YAAY,IACVrL,GAUJ,OATIgL,GACFI,EAAIE,iBAEFP,GACFxiB,EAAQ8a,cAAc+H,GAEpBA,EAAIJ,kBAAoBH,GAC1BA,EAAYS,iBAEPF,CACT,GAEF,SAAS9B,GAAWljB,EAAKmlB,EAAO,CAAC,GAC/B,IAAK,MAAOzlB,EAAKa,KAAUX,OAAOmkB,QAAQoB,GACxC,IACEnlB,EAAIN,GAAOa,CACb,CAAE,MAAO6kB,GACPxlB,OAAOC,eAAeG,EAAKN,EAAK,CAC9B2lB,cAAc,EACdtlB,IAAG,IACMQ,GAGb,CAEF,OAAOP,CACT,CASA,SAASslB,GAAc/kB,GACrB,GAAc,SAAVA,EACF,OAAO,EAET,GAAc,UAAVA,EACF,OAAO,EAET,GAAIA,IAAU4f,OAAO5f,GAAOkC,WAC1B,OAAO0d,OAAO5f,GAEhB,GAAc,KAAVA,GAA0B,SAAVA,EAClB,OAAO,KAET,GAAqB,iBAAVA,EACT,OAAOA,EAET,IACE,OAAOglB,KAAKC,MAAMC,mBAAmBllB,GACvC,CAAE,MAAO6kB,GACP,OAAO7kB,CACT,CACF,CACA,SAASmlB,GAAiBhmB,GACxB,OAAOA,EAAIqO,QAAQ,UAAU4X,GAAO,IAAIA,EAAItjB,iBAC9C,CACA,MAAMujB,GAAc,CAClB,gBAAAC,CAAiB1jB,EAASzC,EAAKa,GAC7B4B,EAAQ6B,aAAa,WAAW0hB,GAAiBhmB,KAAQa,EAC3D,EACA,mBAAAulB,CAAoB3jB,EAASzC,GAC3ByC,EAAQ4B,gBAAgB,WAAW2hB,GAAiBhmB,KACtD,EACA,iBAAAqmB,CAAkB5jB,GAChB,IAAKA,EACH,MAAO,CAAC,EAEV,MAAM0B,EAAa,CAAC,EACdmiB,EAASpmB,OAAO4D,KAAKrB,EAAQ8jB,SAASld,QAAOrJ,GAAOA,EAAI2kB,WAAW,QAAU3kB,EAAI2kB,WAAW,cAClG,IAAK,MAAM3kB,KAAOsmB,EAAQ,CACxB,IAAIE,EAAUxmB,EAAIqO,QAAQ,MAAO,IACjCmY,EAAUA,EAAQC,OAAO,GAAG9jB,cAAgB6jB,EAAQlR,MAAM,EAAGkR,EAAQ5S,QACrEzP,EAAWqiB,GAAWZ,GAAcnjB,EAAQ8jB,QAAQvmB,GACtD,CACA,OAAOmE,CACT,EACAuiB,iBAAgB,CAACjkB,EAASzC,IACjB4lB,GAAcnjB,EAAQic,aAAa,WAAWsH,GAAiBhmB,QAgB1E,MAAM2mB,GAEJ,kBAAWC,GACT,MAAO,CAAC,CACV,CACA,sBAAWC,GACT,MAAO,CAAC,CACV,CACA,eAAWpH,GACT,MAAM,IAAIqH,MAAM,sEAClB,CACA,UAAAC,CAAWC,GAIT,OAHAA,EAAS9D,KAAK+D,gBAAgBD,GAC9BA,EAAS9D,KAAKgE,kBAAkBF,GAChC9D,KAAKiE,iBAAiBH,GACfA,CACT,CACA,iBAAAE,CAAkBF,GAChB,OAAOA,CACT,CACA,eAAAC,CAAgBD,EAAQvkB,GACtB,MAAM2kB,EAAa,GAAU3kB,GAAWyjB,GAAYQ,iBAAiBjkB,EAAS,UAAY,CAAC,EAE3F,MAAO,IACFygB,KAAKmE,YAAYT,WACM,iBAAfQ,EAA0BA,EAAa,CAAC,KAC/C,GAAU3kB,GAAWyjB,GAAYG,kBAAkB5jB,GAAW,CAAC,KAC7C,iBAAXukB,EAAsBA,EAAS,CAAC,EAE/C,CACA,gBAAAG,CAAiBH,EAAQM,EAAcpE,KAAKmE,YAAYR,aACtD,IAAK,MAAO7hB,EAAUuiB,KAAkBrnB,OAAOmkB,QAAQiD,GAAc,CACnE,MAAMzmB,EAAQmmB,EAAOhiB,GACfwiB,EAAY,GAAU3mB,GAAS,UAhiBrC4c,OADSA,EAiiB+C5c,GA/hBnD,GAAG4c,IAELvd,OAAOM,UAAUuC,SAASrC,KAAK+c,GAAQL,MAAM,eAAe,GAAGza,cA8hBlE,IAAK,IAAI8kB,OAAOF,GAAehhB,KAAKihB,GAClC,MAAM,IAAIE,UAAU,GAAGxE,KAAKmE,YAAY5H,KAAKkI,0BAA0B3iB,qBAA4BwiB,yBAAiCD,MAExI,CAriBW9J,KAsiBb,EAqBF,MAAMmK,WAAsBjB,GAC1B,WAAAU,CAAY5kB,EAASukB,GACnBa,SACAplB,EAAUmb,GAAWnb,MAIrBygB,KAAK4E,SAAWrlB,EAChBygB,KAAK6E,QAAU7E,KAAK6D,WAAWC,GAC/BzK,GAAKtH,IAAIiO,KAAK4E,SAAU5E,KAAKmE,YAAYW,SAAU9E,MACrD,CAGA,OAAA+E,GACE1L,GAAKM,OAAOqG,KAAK4E,SAAU5E,KAAKmE,YAAYW,UAC5CvE,GAAaC,IAAIR,KAAK4E,SAAU5E,KAAKmE,YAAYa,WACjD,IAAK,MAAMC,KAAgBjoB,OAAOkoB,oBAAoBlF,MACpDA,KAAKiF,GAAgB,IAEzB,CACA,cAAAE,CAAe9I,EAAU9c,EAAS6lB,GAAa,GAC7CpI,GAAuBX,EAAU9c,EAAS6lB,EAC5C,CACA,UAAAvB,CAAWC,GAIT,OAHAA,EAAS9D,KAAK+D,gBAAgBD,EAAQ9D,KAAK4E,UAC3Cd,EAAS9D,KAAKgE,kBAAkBF,GAChC9D,KAAKiE,iBAAiBH,GACfA,CACT,CAGA,kBAAOuB,CAAY9lB,GACjB,OAAO8Z,GAAKlc,IAAIud,GAAWnb,GAAUygB,KAAK8E,SAC5C,CACA,0BAAOQ,CAAoB/lB,EAASukB,EAAS,CAAC,GAC5C,OAAO9D,KAAKqF,YAAY9lB,IAAY,IAAIygB,KAAKzgB,EAA2B,iBAAXukB,EAAsBA,EAAS,KAC9F,CACA,kBAAWyB,GACT,MA5CY,OA6Cd,CACA,mBAAWT,GACT,MAAO,MAAM9E,KAAKzD,MACpB,CACA,oBAAWyI,GACT,MAAO,IAAIhF,KAAK8E,UAClB,CACA,gBAAOU,CAAUllB,GACf,MAAO,GAAGA,IAAO0f,KAAKgF,WACxB,EAUF,MAAMS,GAAclmB,IAClB,IAAIwa,EAAWxa,EAAQic,aAAa,kBACpC,IAAKzB,GAAyB,MAAbA,EAAkB,CACjC,IAAI2L,EAAgBnmB,EAAQic,aAAa,QAMzC,IAAKkK,IAAkBA,EAActE,SAAS,OAASsE,EAAcjE,WAAW,KAC9E,OAAO,KAILiE,EAActE,SAAS,OAASsE,EAAcjE,WAAW,OAC3DiE,EAAgB,IAAIA,EAAcxjB,MAAM,KAAK,MAE/C6X,EAAW2L,GAAmC,MAAlBA,EAAwBA,EAAcC,OAAS,IAC7E,CACA,OAAO5L,EAAWA,EAAS7X,MAAM,KAAKY,KAAI8iB,GAAO9L,GAAc8L,KAAM1iB,KAAK,KAAO,IAAI,EAEjF2iB,GAAiB,CACrB1T,KAAI,CAAC4H,EAAUxa,EAAU8F,SAASC,kBACzB,GAAG3G,UAAUsB,QAAQ3C,UAAU8iB,iBAAiB5iB,KAAK+B,EAASwa,IAEvE+L,QAAO,CAAC/L,EAAUxa,EAAU8F,SAASC,kBAC5BrF,QAAQ3C,UAAU8K,cAAc5K,KAAK+B,EAASwa,GAEvDgM,SAAQ,CAACxmB,EAASwa,IACT,GAAGpb,UAAUY,EAAQwmB,UAAU5f,QAAOzB,GAASA,EAAMshB,QAAQjM,KAEtE,OAAAkM,CAAQ1mB,EAASwa,GACf,MAAMkM,EAAU,GAChB,IAAIC,EAAW3mB,EAAQwF,WAAWiW,QAAQjB,GAC1C,KAAOmM,GACLD,EAAQrU,KAAKsU,GACbA,EAAWA,EAASnhB,WAAWiW,QAAQjB,GAEzC,OAAOkM,CACT,EACA,IAAAE,CAAK5mB,EAASwa,GACZ,IAAIqM,EAAW7mB,EAAQ8mB,uBACvB,KAAOD,GAAU,CACf,GAAIA,EAASJ,QAAQjM,GACnB,MAAO,CAACqM,GAEVA,EAAWA,EAASC,sBACtB,CACA,MAAO,EACT,EAEA,IAAAxhB,CAAKtF,EAASwa,GACZ,IAAIlV,EAAOtF,EAAQ+mB,mBACnB,KAAOzhB,GAAM,CACX,GAAIA,EAAKmhB,QAAQjM,GACf,MAAO,CAAClV,GAEVA,EAAOA,EAAKyhB,kBACd,CACA,MAAO,EACT,EACA,iBAAAC,CAAkBhnB,GAChB,MAAMinB,EAAa,CAAC,IAAK,SAAU,QAAS,WAAY,SAAU,UAAW,aAAc,4BAA4B1jB,KAAIiX,GAAY,GAAGA,2BAAiC7W,KAAK,KAChL,OAAO8c,KAAK7N,KAAKqU,EAAYjnB,GAAS4G,QAAOsgB,IAAOvL,GAAWuL,IAAO9L,GAAU8L,IAClF,EACA,sBAAAC,CAAuBnnB,GACrB,MAAMwa,EAAW0L,GAAYlmB,GAC7B,OAAIwa,GACK8L,GAAeC,QAAQ/L,GAAYA,EAErC,IACT,EACA,sBAAA4M,CAAuBpnB,GACrB,MAAMwa,EAAW0L,GAAYlmB,GAC7B,OAAOwa,EAAW8L,GAAeC,QAAQ/L,GAAY,IACvD,EACA,+BAAA6M,CAAgCrnB,GAC9B,MAAMwa,EAAW0L,GAAYlmB,GAC7B,OAAOwa,EAAW8L,GAAe1T,KAAK4H,GAAY,EACpD,GAUI8M,GAAuB,CAACC,EAAWC,EAAS,UAChD,MAAMC,EAAa,gBAAgBF,EAAU9B,YACvC1kB,EAAOwmB,EAAUvK,KACvBgE,GAAac,GAAGhc,SAAU2hB,EAAY,qBAAqB1mB,OAAU,SAAU8e,GAI7E,GAHI,CAAC,IAAK,QAAQgC,SAASpB,KAAKiH,UAC9B7H,EAAMkD,iBAEJpH,GAAW8E,MACb,OAEF,MAAMzT,EAASsZ,GAAec,uBAAuB3G,OAASA,KAAKhF,QAAQ,IAAI1a,KAC9DwmB,EAAUxB,oBAAoB/Y,GAGtCwa,IACX,GAAE,EAiBEG,GAAc,YACdC,GAAc,QAAQD,KACtBE,GAAe,SAASF,KAQ9B,MAAMG,WAAc3C,GAElB,eAAWnI,GACT,MAfW,OAgBb,CAGA,KAAA+K,GAEE,GADmB/G,GAAaqB,QAAQ5B,KAAK4E,SAAUuC,IACxCnF,iBACb,OAEFhC,KAAK4E,SAASvJ,UAAU1B,OAlBF,QAmBtB,MAAMyL,EAAapF,KAAK4E,SAASvJ,UAAU7W,SApBrB,QAqBtBwb,KAAKmF,gBAAe,IAAMnF,KAAKuH,mBAAmBvH,KAAK4E,SAAUQ,EACnE,CAGA,eAAAmC,GACEvH,KAAK4E,SAASjL,SACd4G,GAAaqB,QAAQ5B,KAAK4E,SAAUwC,IACpCpH,KAAK+E,SACP,CAGA,sBAAOtI,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOgd,GAAM/B,oBAAoBtF,MACvC,GAAsB,iBAAX8D,EAAX,CAGA,QAAqB/K,IAAjB1O,EAAKyZ,IAAyBA,EAAOrC,WAAW,MAAmB,gBAAXqC,EAC1D,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,GAAQ9D,KAJb,CAKF,GACF,EAOF6G,GAAqBQ,GAAO,SAM5BlL,GAAmBkL,IAcnB,MAKMI,GAAyB,4BAO/B,MAAMC,WAAehD,GAEnB,eAAWnI,GACT,MAfW,QAgBb,CAGA,MAAAoL,GAEE3H,KAAK4E,SAASxjB,aAAa,eAAgB4e,KAAK4E,SAASvJ,UAAUsM,OAjB3C,UAkB1B,CAGA,sBAAOlL,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOqd,GAAOpC,oBAAoBtF,MACzB,WAAX8D,GACFzZ,EAAKyZ,IAET,GACF,EAOFvD,GAAac,GAAGhc,SAjCe,2BAiCmBoiB,IAAwBrI,IACxEA,EAAMkD,iBACN,MAAMsF,EAASxI,EAAM7S,OAAOyO,QAAQyM,IACvBC,GAAOpC,oBAAoBsC,GACnCD,QAAQ,IAOfxL,GAAmBuL,IAcnB,MACMG,GAAc,YACdC,GAAmB,aAAaD,KAChCE,GAAkB,YAAYF,KAC9BG,GAAiB,WAAWH,KAC5BI,GAAoB,cAAcJ,KAClCK,GAAkB,YAAYL,KAK9BM,GAAY,CAChBC,YAAa,KACbC,aAAc,KACdC,cAAe,MAEXC,GAAgB,CACpBH,YAAa,kBACbC,aAAc,kBACdC,cAAe,mBAOjB,MAAME,WAAc/E,GAClB,WAAAU,CAAY5kB,EAASukB,GACnBa,QACA3E,KAAK4E,SAAWrlB,EACXA,GAAYipB,GAAMC,gBAGvBzI,KAAK6E,QAAU7E,KAAK6D,WAAWC,GAC/B9D,KAAK0I,QAAU,EACf1I,KAAK2I,sBAAwB7H,QAAQlhB,OAAOgpB,cAC5C5I,KAAK6I,cACP,CAGA,kBAAWnF,GACT,OAAOyE,EACT,CACA,sBAAWxE,GACT,OAAO4E,EACT,CACA,eAAWhM,GACT,MA/CW,OAgDb,CAGA,OAAAwI,GACExE,GAAaC,IAAIR,KAAK4E,SAAUiD,GAClC,CAGA,MAAAiB,CAAO1J,GACAY,KAAK2I,sBAIN3I,KAAK+I,wBAAwB3J,KAC/BY,KAAK0I,QAAUtJ,EAAM4J,SAJrBhJ,KAAK0I,QAAUtJ,EAAM6J,QAAQ,GAAGD,OAMpC,CACA,IAAAE,CAAK9J,GACCY,KAAK+I,wBAAwB3J,KAC/BY,KAAK0I,QAAUtJ,EAAM4J,QAAUhJ,KAAK0I,SAEtC1I,KAAKmJ,eACLtM,GAAQmD,KAAK6E,QAAQuD,YACvB,CACA,KAAAgB,CAAMhK,GACJY,KAAK0I,QAAUtJ,EAAM6J,SAAW7J,EAAM6J,QAAQvY,OAAS,EAAI,EAAI0O,EAAM6J,QAAQ,GAAGD,QAAUhJ,KAAK0I,OACjG,CACA,YAAAS,GACE,MAAME,EAAYlnB,KAAKoC,IAAIyb,KAAK0I,SAChC,GAAIW,GAnEgB,GAoElB,OAEF,MAAM/b,EAAY+b,EAAYrJ,KAAK0I,QACnC1I,KAAK0I,QAAU,EACVpb,GAGLuP,GAAQvP,EAAY,EAAI0S,KAAK6E,QAAQyD,cAAgBtI,KAAK6E,QAAQwD,aACpE,CACA,WAAAQ,GACM7I,KAAK2I,uBACPpI,GAAac,GAAGrB,KAAK4E,SAAUqD,IAAmB7I,GAASY,KAAK8I,OAAO1J,KACvEmB,GAAac,GAAGrB,KAAK4E,SAAUsD,IAAiB9I,GAASY,KAAKkJ,KAAK9J,KACnEY,KAAK4E,SAASvJ,UAAU5E,IAlFG,mBAoF3B8J,GAAac,GAAGrB,KAAK4E,SAAUkD,IAAkB1I,GAASY,KAAK8I,OAAO1J,KACtEmB,GAAac,GAAGrB,KAAK4E,SAAUmD,IAAiB3I,GAASY,KAAKoJ,MAAMhK,KACpEmB,GAAac,GAAGrB,KAAK4E,SAAUoD,IAAgB5I,GAASY,KAAKkJ,KAAK9J,KAEtE,CACA,uBAAA2J,CAAwB3J,GACtB,OAAOY,KAAK2I,wBA3FS,QA2FiBvJ,EAAMkK,aA5FrB,UA4FyDlK,EAAMkK,YACxF,CAGA,kBAAOb,GACL,MAAO,iBAAkBpjB,SAASC,iBAAmB7C,UAAU8mB,eAAiB,CAClF,EAeF,MAEMC,GAAc,eACdC,GAAiB,YACjBC,GAAmB,YACnBC,GAAoB,aAGpBC,GAAa,OACbC,GAAa,OACbC,GAAiB,OACjBC,GAAkB,QAClBC,GAAc,QAAQR,KACtBS,GAAa,OAAOT,KACpBU,GAAkB,UAAUV,KAC5BW,GAAqB,aAAaX,KAClCY,GAAqB,aAAaZ,KAClCa,GAAmB,YAAYb,KAC/Bc,GAAwB,OAAOd,KAAcC,KAC7Cc,GAAyB,QAAQf,KAAcC,KAC/Ce,GAAsB,WACtBC,GAAsB,SAMtBC,GAAkB,UAClBC,GAAgB,iBAChBC,GAAuBF,GAAkBC,GAKzCE,GAAmB,CACvB,CAACnB,IAAmBK,GACpB,CAACJ,IAAoBG,IAEjBgB,GAAY,CAChBC,SAAU,IACVC,UAAU,EACVC,MAAO,QACPC,MAAM,EACNC,OAAO,EACPC,MAAM,GAEFC,GAAgB,CACpBN,SAAU,mBAEVC,SAAU,UACVC,MAAO,mBACPC,KAAM,mBACNC,MAAO,UACPC,KAAM,WAOR,MAAME,WAAiB5G,GACrB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAKuL,UAAY,KACjBvL,KAAKwL,eAAiB,KACtBxL,KAAKyL,YAAa,EAClBzL,KAAK0L,aAAe,KACpB1L,KAAK2L,aAAe,KACpB3L,KAAK4L,mBAAqB/F,GAAeC,QArCjB,uBAqC8C9F,KAAK4E,UAC3E5E,KAAK6L,qBACD7L,KAAK6E,QAAQqG,OAASV,IACxBxK,KAAK8L,OAET,CAGA,kBAAWpI,GACT,OAAOoH,EACT,CACA,sBAAWnH,GACT,OAAO0H,EACT,CACA,eAAW9O,GACT,MAnFW,UAoFb,CAGA,IAAA1X,GACEmb,KAAK+L,OAAOnC,GACd,CACA,eAAAoC,IAIO3mB,SAAS4mB,QAAUtR,GAAUqF,KAAK4E,WACrC5E,KAAKnb,MAET,CACA,IAAAshB,GACEnG,KAAK+L,OAAOlC,GACd,CACA,KAAAoB,GACMjL,KAAKyL,YACPrR,GAAqB4F,KAAK4E,UAE5B5E,KAAKkM,gBACP,CACA,KAAAJ,GACE9L,KAAKkM,iBACLlM,KAAKmM,kBACLnM,KAAKuL,UAAYa,aAAY,IAAMpM,KAAKgM,mBAAmBhM,KAAK6E,QAAQkG,SAC1E,CACA,iBAAAsB,GACOrM,KAAK6E,QAAQqG,OAGdlL,KAAKyL,WACPlL,GAAae,IAAItB,KAAK4E,SAAUqF,IAAY,IAAMjK,KAAK8L,UAGzD9L,KAAK8L,QACP,CACA,EAAAQ,CAAG7T,GACD,MAAM8T,EAAQvM,KAAKwM,YACnB,GAAI/T,EAAQ8T,EAAM7b,OAAS,GAAK+H,EAAQ,EACtC,OAEF,GAAIuH,KAAKyL,WAEP,YADAlL,GAAae,IAAItB,KAAK4E,SAAUqF,IAAY,IAAMjK,KAAKsM,GAAG7T,KAG5D,MAAMgU,EAAczM,KAAK0M,cAAc1M,KAAK2M,cAC5C,GAAIF,IAAgBhU,EAClB,OAEF,MAAMtC,EAAQsC,EAAQgU,EAAc7C,GAAaC,GACjD7J,KAAK+L,OAAO5V,EAAOoW,EAAM9T,GAC3B,CACA,OAAAsM,GACM/E,KAAK2L,cACP3L,KAAK2L,aAAa5G,UAEpBJ,MAAMI,SACR,CAGA,iBAAAf,CAAkBF,GAEhB,OADAA,EAAO8I,gBAAkB9I,EAAOiH,SACzBjH,CACT,CACA,kBAAA+H,GACM7L,KAAK6E,QAAQmG,UACfzK,GAAac,GAAGrB,KAAK4E,SAAUsF,IAAiB9K,GAASY,KAAK6M,SAASzN,KAE9C,UAAvBY,KAAK6E,QAAQoG,QACf1K,GAAac,GAAGrB,KAAK4E,SAAUuF,IAAoB,IAAMnK,KAAKiL,UAC9D1K,GAAac,GAAGrB,KAAK4E,SAAUwF,IAAoB,IAAMpK,KAAKqM,uBAE5DrM,KAAK6E,QAAQsG,OAAS3C,GAAMC,eAC9BzI,KAAK8M,yBAET,CACA,uBAAAA,GACE,IAAK,MAAMC,KAAOlH,GAAe1T,KArIX,qBAqImC6N,KAAK4E,UAC5DrE,GAAac,GAAG0L,EAAK1C,IAAkBjL,GAASA,EAAMkD,mBAExD,MAmBM0K,EAAc,CAClB3E,aAAc,IAAMrI,KAAK+L,OAAO/L,KAAKiN,kBAAkBnD,KACvDxB,cAAe,IAAMtI,KAAK+L,OAAO/L,KAAKiN,kBAAkBlD,KACxD3B,YAtBkB,KACS,UAAvBpI,KAAK6E,QAAQoG,QAYjBjL,KAAKiL,QACDjL,KAAK0L,cACPwB,aAAalN,KAAK0L,cAEpB1L,KAAK0L,aAAe7N,YAAW,IAAMmC,KAAKqM,qBAjLjB,IAiL+DrM,KAAK6E,QAAQkG,UAAS,GAOhH/K,KAAK2L,aAAe,IAAInD,GAAMxI,KAAK4E,SAAUoI,EAC/C,CACA,QAAAH,CAASzN,GACP,GAAI,kBAAkB/b,KAAK+b,EAAM7S,OAAO0a,SACtC,OAEF,MAAM3Z,EAAYud,GAAiBzL,EAAMtiB,KACrCwQ,IACF8R,EAAMkD,iBACNtC,KAAK+L,OAAO/L,KAAKiN,kBAAkB3f,IAEvC,CACA,aAAAof,CAAcntB,GACZ,OAAOygB,KAAKwM,YAAYrnB,QAAQ5F,EAClC,CACA,0BAAA4tB,CAA2B1U,GACzB,IAAKuH,KAAK4L,mBACR,OAEF,MAAMwB,EAAkBvH,GAAeC,QAAQ4E,GAAiB1K,KAAK4L,oBACrEwB,EAAgB/R,UAAU1B,OAAO8Q,IACjC2C,EAAgBjsB,gBAAgB,gBAChC,MAAMksB,EAAqBxH,GAAeC,QAAQ,sBAAsBrN,MAAWuH,KAAK4L,oBACpFyB,IACFA,EAAmBhS,UAAU5E,IAAIgU,IACjC4C,EAAmBjsB,aAAa,eAAgB,QAEpD,CACA,eAAA+qB,GACE,MAAM5sB,EAAUygB,KAAKwL,gBAAkBxL,KAAK2M,aAC5C,IAAKptB,EACH,OAEF,MAAM+tB,EAAkB/P,OAAOgQ,SAAShuB,EAAQic,aAAa,oBAAqB,IAClFwE,KAAK6E,QAAQkG,SAAWuC,GAAmBtN,KAAK6E,QAAQ+H,eAC1D,CACA,MAAAb,CAAO5V,EAAO5W,EAAU,MACtB,GAAIygB,KAAKyL,WACP,OAEF,MAAM1N,EAAgBiC,KAAK2M,aACrBa,EAASrX,IAAUyT,GACnB6D,EAAcluB,GAAWue,GAAqBkC,KAAKwM,YAAazO,EAAeyP,EAAQxN,KAAK6E,QAAQuG,MAC1G,GAAIqC,IAAgB1P,EAClB,OAEF,MAAM2P,EAAmB1N,KAAK0M,cAAce,GACtCE,EAAenI,GACZjF,GAAaqB,QAAQ5B,KAAK4E,SAAUY,EAAW,CACpD1F,cAAe2N,EACfngB,UAAW0S,KAAK4N,kBAAkBzX,GAClCuD,KAAMsG,KAAK0M,cAAc3O,GACzBuO,GAAIoB,IAIR,GADmBC,EAAa3D,IACjBhI,iBACb,OAEF,IAAKjE,IAAkB0P,EAGrB,OAEF,MAAMI,EAAY/M,QAAQd,KAAKuL,WAC/BvL,KAAKiL,QACLjL,KAAKyL,YAAa,EAClBzL,KAAKmN,2BAA2BO,GAChC1N,KAAKwL,eAAiBiC,EACtB,MAAMK,EAAuBN,EA3OR,sBADF,oBA6ObO,EAAiBP,EA3OH,qBACA,qBA2OpBC,EAAYpS,UAAU5E,IAAIsX,GAC1BlS,GAAO4R,GACP1P,EAAc1C,UAAU5E,IAAIqX,GAC5BL,EAAYpS,UAAU5E,IAAIqX,GAQ1B9N,KAAKmF,gBAPoB,KACvBsI,EAAYpS,UAAU1B,OAAOmU,EAAsBC,GACnDN,EAAYpS,UAAU5E,IAAIgU,IAC1B1M,EAAc1C,UAAU1B,OAAO8Q,GAAqBsD,EAAgBD,GACpE9N,KAAKyL,YAAa,EAClBkC,EAAa1D,GAAW,GAEYlM,EAAeiC,KAAKgO,eACtDH,GACF7N,KAAK8L,OAET,CACA,WAAAkC,GACE,OAAOhO,KAAK4E,SAASvJ,UAAU7W,SAhQV,QAiQvB,CACA,UAAAmoB,GACE,OAAO9G,GAAeC,QAAQ8E,GAAsB5K,KAAK4E,SAC3D,CACA,SAAA4H,GACE,OAAO3G,GAAe1T,KAAKwY,GAAe3K,KAAK4E,SACjD,CACA,cAAAsH,GACMlM,KAAKuL,YACP0C,cAAcjO,KAAKuL,WACnBvL,KAAKuL,UAAY,KAErB,CACA,iBAAA0B,CAAkB3f,GAChB,OAAI2O,KACK3O,IAAcwc,GAAiBD,GAAaD,GAE9Ctc,IAAcwc,GAAiBF,GAAaC,EACrD,CACA,iBAAA+D,CAAkBzX,GAChB,OAAI8F,KACK9F,IAAU0T,GAAaC,GAAiBC,GAE1C5T,IAAU0T,GAAaE,GAAkBD,EAClD,CAGA,sBAAOrN,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOihB,GAAShG,oBAAoBtF,KAAM8D,GAChD,GAAsB,iBAAXA,GAIX,GAAsB,iBAAXA,EAAqB,CAC9B,QAAqB/K,IAAjB1O,EAAKyZ,IAAyBA,EAAOrC,WAAW,MAAmB,gBAAXqC,EAC1D,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IACP,OAREzZ,EAAKiiB,GAAGxI,EASZ,GACF,EAOFvD,GAAac,GAAGhc,SAAUklB,GAvSE,uCAuS2C,SAAUnL,GAC/E,MAAM7S,EAASsZ,GAAec,uBAAuB3G,MACrD,IAAKzT,IAAWA,EAAO8O,UAAU7W,SAASgmB,IACxC,OAEFpL,EAAMkD,iBACN,MAAM4L,EAAW5C,GAAShG,oBAAoB/Y,GACxC4hB,EAAanO,KAAKxE,aAAa,oBACrC,OAAI2S,GACFD,EAAS5B,GAAG6B,QACZD,EAAS7B,qBAGyC,SAAhDrJ,GAAYQ,iBAAiBxD,KAAM,UACrCkO,EAASrpB,YACTqpB,EAAS7B,sBAGX6B,EAAS/H,YACT+H,EAAS7B,oBACX,IACA9L,GAAac,GAAGzhB,OAAQ0qB,IAAuB,KAC7C,MAAM8D,EAAYvI,GAAe1T,KA5TR,6BA6TzB,IAAK,MAAM+b,KAAYE,EACrB9C,GAAShG,oBAAoB4I,EAC/B,IAOF/R,GAAmBmP,IAcnB,MAEM+C,GAAc,eAEdC,GAAe,OAAOD,KACtBE,GAAgB,QAAQF,KACxBG,GAAe,OAAOH,KACtBI,GAAiB,SAASJ,KAC1BK,GAAyB,QAAQL,cACjCM,GAAoB,OACpBC,GAAsB,WACtBC,GAAwB,aAExBC,GAA6B,WAAWF,OAAwBA,KAKhEG,GAAyB,8BACzBC,GAAY,CAChBvqB,OAAQ,KACRkjB,QAAQ,GAEJsH,GAAgB,CACpBxqB,OAAQ,iBACRkjB,OAAQ,WAOV,MAAMuH,WAAiBxK,GACrB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAKmP,kBAAmB,EACxBnP,KAAKoP,cAAgB,GACrB,MAAMC,EAAaxJ,GAAe1T,KAAK4c,IACvC,IAAK,MAAMO,KAAQD,EAAY,CAC7B,MAAMtV,EAAW8L,GAAea,uBAAuB4I,GACjDC,EAAgB1J,GAAe1T,KAAK4H,GAAU5T,QAAOqpB,GAAgBA,IAAiBxP,KAAK4E,WAChF,OAAb7K,GAAqBwV,EAAc7e,QACrCsP,KAAKoP,cAAcxd,KAAK0d,EAE5B,CACAtP,KAAKyP,sBACAzP,KAAK6E,QAAQpgB,QAChBub,KAAK0P,0BAA0B1P,KAAKoP,cAAepP,KAAK2P,YAEtD3P,KAAK6E,QAAQ8C,QACf3H,KAAK2H,QAET,CAGA,kBAAWjE,GACT,OAAOsL,EACT,CACA,sBAAWrL,GACT,OAAOsL,EACT,CACA,eAAW1S,GACT,MA9DW,UA+Db,CAGA,MAAAoL,GACM3H,KAAK2P,WACP3P,KAAK4P,OAEL5P,KAAK6P,MAET,CACA,IAAAA,GACE,GAAI7P,KAAKmP,kBAAoBnP,KAAK2P,WAChC,OAEF,IAAIG,EAAiB,GAQrB,GALI9P,KAAK6E,QAAQpgB,SACfqrB,EAAiB9P,KAAK+P,uBAhEH,wCAgE4C5pB,QAAO5G,GAAWA,IAAYygB,KAAK4E,WAAU9hB,KAAIvD,GAAW2vB,GAAS5J,oBAAoB/lB,EAAS,CAC/JooB,QAAQ,OAGRmI,EAAepf,QAAUof,EAAe,GAAGX,iBAC7C,OAGF,GADmB5O,GAAaqB,QAAQ5B,KAAK4E,SAAU0J,IACxCtM,iBACb,OAEF,IAAK,MAAMgO,KAAkBF,EAC3BE,EAAeJ,OAEjB,MAAMK,EAAYjQ,KAAKkQ,gBACvBlQ,KAAK4E,SAASvJ,UAAU1B,OAAOiV,IAC/B5O,KAAK4E,SAASvJ,UAAU5E,IAAIoY,IAC5B7O,KAAK4E,SAAS7jB,MAAMkvB,GAAa,EACjCjQ,KAAK0P,0BAA0B1P,KAAKoP,eAAe,GACnDpP,KAAKmP,kBAAmB,EACxB,MAQMgB,EAAa,SADUF,EAAU,GAAGxL,cAAgBwL,EAAU7d,MAAM,KAE1E4N,KAAKmF,gBATY,KACfnF,KAAKmP,kBAAmB,EACxBnP,KAAK4E,SAASvJ,UAAU1B,OAAOkV,IAC/B7O,KAAK4E,SAASvJ,UAAU5E,IAAImY,GAAqBD,IACjD3O,KAAK4E,SAAS7jB,MAAMkvB,GAAa,GACjC1P,GAAaqB,QAAQ5B,KAAK4E,SAAU2J,GAAc,GAItBvO,KAAK4E,UAAU,GAC7C5E,KAAK4E,SAAS7jB,MAAMkvB,GAAa,GAAGjQ,KAAK4E,SAASuL,MACpD,CACA,IAAAP,GACE,GAAI5P,KAAKmP,mBAAqBnP,KAAK2P,WACjC,OAGF,GADmBpP,GAAaqB,QAAQ5B,KAAK4E,SAAU4J,IACxCxM,iBACb,OAEF,MAAMiO,EAAYjQ,KAAKkQ,gBACvBlQ,KAAK4E,SAAS7jB,MAAMkvB,GAAa,GAAGjQ,KAAK4E,SAASthB,wBAAwB2sB,OAC1EpU,GAAOmE,KAAK4E,UACZ5E,KAAK4E,SAASvJ,UAAU5E,IAAIoY,IAC5B7O,KAAK4E,SAASvJ,UAAU1B,OAAOiV,GAAqBD,IACpD,IAAK,MAAM/M,KAAW5B,KAAKoP,cAAe,CACxC,MAAM7vB,EAAUsmB,GAAec,uBAAuB/E,GAClDriB,IAAYygB,KAAK2P,SAASpwB,IAC5BygB,KAAK0P,0BAA0B,CAAC9N,IAAU,EAE9C,CACA5B,KAAKmP,kBAAmB,EAOxBnP,KAAK4E,SAAS7jB,MAAMkvB,GAAa,GACjCjQ,KAAKmF,gBAPY,KACfnF,KAAKmP,kBAAmB,EACxBnP,KAAK4E,SAASvJ,UAAU1B,OAAOkV,IAC/B7O,KAAK4E,SAASvJ,UAAU5E,IAAImY,IAC5BrO,GAAaqB,QAAQ5B,KAAK4E,SAAU6J,GAAe,GAGvBzO,KAAK4E,UAAU,EAC/C,CACA,QAAA+K,CAASpwB,EAAUygB,KAAK4E,UACtB,OAAOrlB,EAAQ8b,UAAU7W,SAASmqB,GACpC,CAGA,iBAAA3K,CAAkBF,GAGhB,OAFAA,EAAO6D,OAAS7G,QAAQgD,EAAO6D,QAC/B7D,EAAOrf,OAASiW,GAAWoJ,EAAOrf,QAC3Bqf,CACT,CACA,aAAAoM,GACE,OAAOlQ,KAAK4E,SAASvJ,UAAU7W,SA3IL,uBAChB,QACC,QA0Ib,CACA,mBAAAirB,GACE,IAAKzP,KAAK6E,QAAQpgB,OAChB,OAEF,MAAMshB,EAAW/F,KAAK+P,uBAAuBhB,IAC7C,IAAK,MAAMxvB,KAAWwmB,EAAU,CAC9B,MAAMqK,EAAWvK,GAAec,uBAAuBpnB,GACnD6wB,GACFpQ,KAAK0P,0BAA0B,CAACnwB,GAAUygB,KAAK2P,SAASS,GAE5D,CACF,CACA,sBAAAL,CAAuBhW,GACrB,MAAMgM,EAAWF,GAAe1T,KAAK2c,GAA4B9O,KAAK6E,QAAQpgB,QAE9E,OAAOohB,GAAe1T,KAAK4H,EAAUiG,KAAK6E,QAAQpgB,QAAQ0B,QAAO5G,IAAYwmB,EAAS3E,SAAS7hB,IACjG,CACA,yBAAAmwB,CAA0BW,EAAcC,GACtC,GAAKD,EAAa3f,OAGlB,IAAK,MAAMnR,KAAW8wB,EACpB9wB,EAAQ8b,UAAUsM,OArKK,aAqKyB2I,GAChD/wB,EAAQ6B,aAAa,gBAAiBkvB,EAE1C,CAGA,sBAAO7T,CAAgBqH,GACrB,MAAMe,EAAU,CAAC,EAIjB,MAHsB,iBAAXf,GAAuB,YAAYzgB,KAAKygB,KACjDe,EAAQ8C,QAAS,GAEZ3H,KAAKwH,MAAK,WACf,MAAMnd,EAAO6kB,GAAS5J,oBAAoBtF,KAAM6E,GAChD,GAAsB,iBAAXf,EAAqB,CAC9B,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IACP,CACF,GACF,EAOFvD,GAAac,GAAGhc,SAAUqpB,GAAwBK,IAAwB,SAAU3P,IAErD,MAAzBA,EAAM7S,OAAO0a,SAAmB7H,EAAMW,gBAAmD,MAAjCX,EAAMW,eAAekH,UAC/E7H,EAAMkD,iBAER,IAAK,MAAM/iB,KAAWsmB,GAAee,gCAAgC5G,MACnEkP,GAAS5J,oBAAoB/lB,EAAS,CACpCooB,QAAQ,IACPA,QAEP,IAMAxL,GAAmB+S,IAcnB,MAAMqB,GAAS,WAETC,GAAc,eACdC,GAAiB,YAGjBC,GAAiB,UACjBC,GAAmB,YAGnBC,GAAe,OAAOJ,KACtBK,GAAiB,SAASL,KAC1BM,GAAe,OAAON,KACtBO,GAAgB,QAAQP,KACxBQ,GAAyB,QAAQR,KAAcC,KAC/CQ,GAAyB,UAAUT,KAAcC,KACjDS,GAAuB,QAAQV,KAAcC,KAC7CU,GAAoB,OAMpBC,GAAyB,4DACzBC,GAA6B,GAAGD,MAA0BD,KAC1DG,GAAgB,iBAIhBC,GAAgBtV,KAAU,UAAY,YACtCuV,GAAmBvV,KAAU,YAAc,UAC3CwV,GAAmBxV,KAAU,aAAe,eAC5CyV,GAAsBzV,KAAU,eAAiB,aACjD0V,GAAkB1V,KAAU,aAAe,cAC3C2V,GAAiB3V,KAAU,cAAgB,aAG3C4V,GAAY,CAChBC,WAAW,EACX7jB,SAAU,kBACV8jB,QAAS,UACT/pB,OAAQ,CAAC,EAAG,GACZgqB,aAAc,KACd1zB,UAAW,UAEP2zB,GAAgB,CACpBH,UAAW,mBACX7jB,SAAU,mBACV8jB,QAAS,SACT/pB,OAAQ,0BACRgqB,aAAc,yBACd1zB,UAAW,2BAOb,MAAM4zB,WAAiBxN,GACrB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAKmS,QAAU,KACfnS,KAAKoS,QAAUpS,KAAK4E,SAAS7f,WAE7Bib,KAAKqS,MAAQxM,GAAehhB,KAAKmb,KAAK4E,SAAU0M,IAAe,IAAMzL,GAAeM,KAAKnG,KAAK4E,SAAU0M,IAAe,IAAMzL,GAAeC,QAAQwL,GAAetR,KAAKoS,SACxKpS,KAAKsS,UAAYtS,KAAKuS,eACxB,CAGA,kBAAW7O,GACT,OAAOmO,EACT,CACA,sBAAWlO,GACT,OAAOsO,EACT,CACA,eAAW1V,GACT,OAAOgU,EACT,CAGA,MAAA5I,GACE,OAAO3H,KAAK2P,WAAa3P,KAAK4P,OAAS5P,KAAK6P,MAC9C,CACA,IAAAA,GACE,GAAI3U,GAAW8E,KAAK4E,WAAa5E,KAAK2P,WACpC,OAEF,MAAM7P,EAAgB,CACpBA,cAAeE,KAAK4E,UAGtB,IADkBrE,GAAaqB,QAAQ5B,KAAK4E,SAAUkM,GAAchR,GACtDkC,iBAAd,CASA,GANAhC,KAAKwS,gBAMD,iBAAkBntB,SAASC,kBAAoB0a,KAAKoS,QAAQpX,QAzExC,eA0EtB,IAAK,MAAMzb,IAAW,GAAGZ,UAAU0G,SAAS6G,KAAK6Z,UAC/CxF,GAAac,GAAG9hB,EAAS,YAAaqc,IAG1CoE,KAAK4E,SAAS6N,QACdzS,KAAK4E,SAASxjB,aAAa,iBAAiB,GAC5C4e,KAAKqS,MAAMhX,UAAU5E,IAAI0a,IACzBnR,KAAK4E,SAASvJ,UAAU5E,IAAI0a,IAC5B5Q,GAAaqB,QAAQ5B,KAAK4E,SAAUmM,GAAejR,EAhBnD,CAiBF,CACA,IAAA8P,GACE,GAAI1U,GAAW8E,KAAK4E,YAAc5E,KAAK2P,WACrC,OAEF,MAAM7P,EAAgB,CACpBA,cAAeE,KAAK4E,UAEtB5E,KAAK0S,cAAc5S,EACrB,CACA,OAAAiF,GACM/E,KAAKmS,SACPnS,KAAKmS,QAAQnZ,UAEf2L,MAAMI,SACR,CACA,MAAAha,GACEiV,KAAKsS,UAAYtS,KAAKuS,gBAClBvS,KAAKmS,SACPnS,KAAKmS,QAAQpnB,QAEjB,CAGA,aAAA2nB,CAAc5S,GAEZ,IADkBS,GAAaqB,QAAQ5B,KAAK4E,SAAUgM,GAAc9Q,GACtDkC,iBAAd,CAMA,GAAI,iBAAkB3c,SAASC,gBAC7B,IAAK,MAAM/F,IAAW,GAAGZ,UAAU0G,SAAS6G,KAAK6Z,UAC/CxF,GAAaC,IAAIjhB,EAAS,YAAaqc,IAGvCoE,KAAKmS,SACPnS,KAAKmS,QAAQnZ,UAEfgH,KAAKqS,MAAMhX,UAAU1B,OAAOwX,IAC5BnR,KAAK4E,SAASvJ,UAAU1B,OAAOwX,IAC/BnR,KAAK4E,SAASxjB,aAAa,gBAAiB,SAC5C4hB,GAAYE,oBAAoBlD,KAAKqS,MAAO,UAC5C9R,GAAaqB,QAAQ5B,KAAK4E,SAAUiM,GAAgB/Q,EAhBpD,CAiBF,CACA,UAAA+D,CAAWC,GAET,GAAgC,iBADhCA,EAASa,MAAMd,WAAWC,IACRxlB,YAA2B,GAAUwlB,EAAOxlB,YAAgE,mBAA3CwlB,EAAOxlB,UAAUgF,sBAElG,MAAM,IAAIkhB,UAAU,GAAG+L,GAAO9L,+GAEhC,OAAOX,CACT,CACA,aAAA0O,GACE,QAAsB,IAAX,EACT,MAAM,IAAIhO,UAAU,gEAEtB,IAAImO,EAAmB3S,KAAK4E,SACG,WAA3B5E,KAAK6E,QAAQvmB,UACfq0B,EAAmB3S,KAAKoS,QACf,GAAUpS,KAAK6E,QAAQvmB,WAChCq0B,EAAmBjY,GAAWsF,KAAK6E,QAAQvmB,WACA,iBAA3B0hB,KAAK6E,QAAQvmB,YAC7Bq0B,EAAmB3S,KAAK6E,QAAQvmB,WAElC,MAAM0zB,EAAehS,KAAK4S,mBAC1B5S,KAAKmS,QAAU,GAAoBQ,EAAkB3S,KAAKqS,MAAOL,EACnE,CACA,QAAArC,GACE,OAAO3P,KAAKqS,MAAMhX,UAAU7W,SAAS2sB,GACvC,CACA,aAAA0B,GACE,MAAMC,EAAiB9S,KAAKoS,QAC5B,GAAIU,EAAezX,UAAU7W,SArKN,WAsKrB,OAAOmtB,GAET,GAAImB,EAAezX,UAAU7W,SAvKJ,aAwKvB,OAAOotB,GAET,GAAIkB,EAAezX,UAAU7W,SAzKA,iBA0K3B,MA5JsB,MA8JxB,GAAIsuB,EAAezX,UAAU7W,SA3KE,mBA4K7B,MA9JyB,SAkK3B,MAAMuuB,EAAkF,QAA1E9tB,iBAAiB+a,KAAKqS,OAAOvX,iBAAiB,iBAAiB6K,OAC7E,OAAImN,EAAezX,UAAU7W,SArLP,UAsLbuuB,EAAQvB,GAAmBD,GAE7BwB,EAAQrB,GAAsBD,EACvC,CACA,aAAAc,GACE,OAAkD,OAA3CvS,KAAK4E,SAAS5J,QAnLD,UAoLtB,CACA,UAAAgY,GACE,MAAM,OACJhrB,GACEgY,KAAK6E,QACT,MAAsB,iBAAX7c,EACFA,EAAO9F,MAAM,KAAKY,KAAInF,GAAS4f,OAAOgQ,SAAS5vB,EAAO,MAEzC,mBAAXqK,EACFirB,GAAcjrB,EAAOirB,EAAYjT,KAAK4E,UAExC5c,CACT,CACA,gBAAA4qB,GACE,MAAMM,EAAwB,CAC5Bx0B,UAAWshB,KAAK6S,gBAChBzc,UAAW,CAAC,CACV9V,KAAM,kBACNmB,QAAS,CACPwM,SAAU+R,KAAK6E,QAAQ5W,WAExB,CACD3N,KAAM,SACNmB,QAAS,CACPuG,OAAQgY,KAAKgT,iBAanB,OAPIhT,KAAKsS,WAAsC,WAAzBtS,KAAK6E,QAAQkN,WACjC/O,GAAYC,iBAAiBjD,KAAKqS,MAAO,SAAU,UACnDa,EAAsB9c,UAAY,CAAC,CACjC9V,KAAM,cACNC,SAAS,KAGN,IACF2yB,KACArW,GAAQmD,KAAK6E,QAAQmN,aAAc,CAACkB,IAE3C,CACA,eAAAC,EAAgB,IACdr2B,EAAG,OACHyP,IAEA,MAAMggB,EAAQ1G,GAAe1T,KAhOF,8DAgO+B6N,KAAKqS,OAAOlsB,QAAO5G,GAAWob,GAAUpb,KAC7FgtB,EAAM7b,QAMXoN,GAAqByO,EAAOhgB,EAAQzP,IAAQ6zB,IAAmBpE,EAAMnL,SAAS7U,IAASkmB,OACzF,CAGA,sBAAOhW,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAO6nB,GAAS5M,oBAAoBtF,KAAM8D,GAChD,GAAsB,iBAAXA,EAAX,CAGA,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IAJL,CAKF,GACF,CACA,iBAAOsP,CAAWhU,GAChB,GA5QuB,IA4QnBA,EAAMwI,QAAgD,UAAfxI,EAAMqB,MA/QnC,QA+QuDrB,EAAMtiB,IACzE,OAEF,MAAMu2B,EAAcxN,GAAe1T,KAAKkf,IACxC,IAAK,MAAM1J,KAAU0L,EAAa,CAChC,MAAMC,EAAUpB,GAAS7M,YAAYsC,GACrC,IAAK2L,IAAyC,IAA9BA,EAAQzO,QAAQiN,UAC9B,SAEF,MAAMyB,EAAenU,EAAMmU,eACrBC,EAAeD,EAAanS,SAASkS,EAAQjB,OACnD,GAAIkB,EAAanS,SAASkS,EAAQ1O,WAA2C,WAA9B0O,EAAQzO,QAAQiN,YAA2B0B,GAA8C,YAA9BF,EAAQzO,QAAQiN,WAA2B0B,EACnJ,SAIF,GAAIF,EAAQjB,MAAM7tB,SAAS4a,EAAM7S,UAA2B,UAAf6S,EAAMqB,MA/RvC,QA+R2DrB,EAAMtiB,KAAqB,qCAAqCuG,KAAK+b,EAAM7S,OAAO0a,UACvJ,SAEF,MAAMnH,EAAgB,CACpBA,cAAewT,EAAQ1O,UAEN,UAAfxF,EAAMqB,OACRX,EAAckH,WAAa5H,GAE7BkU,EAAQZ,cAAc5S,EACxB,CACF,CACA,4BAAO2T,CAAsBrU,GAI3B,MAAMsU,EAAU,kBAAkBrwB,KAAK+b,EAAM7S,OAAO0a,SAC9C0M,EAjTW,WAiTKvU,EAAMtiB,IACtB82B,EAAkB,CAAClD,GAAgBC,IAAkBvP,SAAShC,EAAMtiB,KAC1E,IAAK82B,IAAoBD,EACvB,OAEF,GAAID,IAAYC,EACd,OAEFvU,EAAMkD,iBAGN,MAAMuR,EAAkB7T,KAAKgG,QAAQoL,IAA0BpR,KAAO6F,GAAeM,KAAKnG,KAAMoR,IAAwB,IAAMvL,GAAehhB,KAAKmb,KAAMoR,IAAwB,IAAMvL,GAAeC,QAAQsL,GAAwBhS,EAAMW,eAAehb,YACpPwF,EAAW2nB,GAAS5M,oBAAoBuO,GAC9C,GAAID,EAIF,OAHAxU,EAAM0U,kBACNvpB,EAASslB,YACTtlB,EAAS4oB,gBAAgB/T,GAGvB7U,EAASolB,aAEXvQ,EAAM0U,kBACNvpB,EAASqlB,OACTiE,EAAgBpB,QAEpB,EAOFlS,GAAac,GAAGhc,SAAU4rB,GAAwBG,GAAwBc,GAASuB,uBACnFlT,GAAac,GAAGhc,SAAU4rB,GAAwBK,GAAeY,GAASuB,uBAC1ElT,GAAac,GAAGhc,SAAU2rB,GAAwBkB,GAASkB,YAC3D7S,GAAac,GAAGhc,SAAU6rB,GAAsBgB,GAASkB,YACzD7S,GAAac,GAAGhc,SAAU2rB,GAAwBI,IAAwB,SAAUhS,GAClFA,EAAMkD,iBACN4P,GAAS5M,oBAAoBtF,MAAM2H,QACrC,IAMAxL,GAAmB+V,IAcnB,MAAM6B,GAAS,WAETC,GAAoB,OACpBC,GAAkB,gBAAgBF,KAClCG,GAAY,CAChBC,UAAW,iBACXC,cAAe,KACfhP,YAAY,EACZzK,WAAW,EAEX0Z,YAAa,QAETC,GAAgB,CACpBH,UAAW,SACXC,cAAe,kBACfhP,WAAY,UACZzK,UAAW,UACX0Z,YAAa,oBAOf,MAAME,WAAiB9Q,GACrB,WAAAU,CAAYL,GACVa,QACA3E,KAAK6E,QAAU7E,KAAK6D,WAAWC,GAC/B9D,KAAKwU,aAAc,EACnBxU,KAAK4E,SAAW,IAClB,CAGA,kBAAWlB,GACT,OAAOwQ,EACT,CACA,sBAAWvQ,GACT,OAAO2Q,EACT,CACA,eAAW/X,GACT,OAAOwX,EACT,CAGA,IAAAlE,CAAKxT,GACH,IAAK2D,KAAK6E,QAAQlK,UAEhB,YADAkC,GAAQR,GAGV2D,KAAKyU,UACL,MAAMl1B,EAAUygB,KAAK0U,cACjB1U,KAAK6E,QAAQO,YACfvJ,GAAOtc,GAETA,EAAQ8b,UAAU5E,IAAIud,IACtBhU,KAAK2U,mBAAkB,KACrB9X,GAAQR,EAAS,GAErB,CACA,IAAAuT,CAAKvT,GACE2D,KAAK6E,QAAQlK,WAIlBqF,KAAK0U,cAAcrZ,UAAU1B,OAAOqa,IACpChU,KAAK2U,mBAAkB,KACrB3U,KAAK+E,UACLlI,GAAQR,EAAS,KANjBQ,GAAQR,EAQZ,CACA,OAAA0I,GACO/E,KAAKwU,cAGVjU,GAAaC,IAAIR,KAAK4E,SAAUqP,IAChCjU,KAAK4E,SAASjL,SACdqG,KAAKwU,aAAc,EACrB,CAGA,WAAAE,GACE,IAAK1U,KAAK4E,SAAU,CAClB,MAAMgQ,EAAWvvB,SAASwvB,cAAc,OACxCD,EAAST,UAAYnU,KAAK6E,QAAQsP,UAC9BnU,KAAK6E,QAAQO,YACfwP,EAASvZ,UAAU5E,IApFD,QAsFpBuJ,KAAK4E,SAAWgQ,CAClB,CACA,OAAO5U,KAAK4E,QACd,CACA,iBAAAZ,CAAkBF,GAGhB,OADAA,EAAOuQ,YAAc3Z,GAAWoJ,EAAOuQ,aAChCvQ,CACT,CACA,OAAA2Q,GACE,GAAIzU,KAAKwU,YACP,OAEF,MAAMj1B,EAAUygB,KAAK0U,cACrB1U,KAAK6E,QAAQwP,YAAYS,OAAOv1B,GAChCghB,GAAac,GAAG9hB,EAAS00B,IAAiB,KACxCpX,GAAQmD,KAAK6E,QAAQuP,cAAc,IAErCpU,KAAKwU,aAAc,CACrB,CACA,iBAAAG,CAAkBtY,GAChBW,GAAuBX,EAAU2D,KAAK0U,cAAe1U,KAAK6E,QAAQO,WACpE,EAeF,MAEM2P,GAAc,gBACdC,GAAkB,UAAUD,KAC5BE,GAAoB,cAAcF,KAGlCG,GAAmB,WACnBC,GAAY,CAChBC,WAAW,EACXC,YAAa,MAETC,GAAgB,CACpBF,UAAW,UACXC,YAAa,WAOf,MAAME,WAAkB9R,GACtB,WAAAU,CAAYL,GACVa,QACA3E,KAAK6E,QAAU7E,KAAK6D,WAAWC,GAC/B9D,KAAKwV,WAAY,EACjBxV,KAAKyV,qBAAuB,IAC9B,CAGA,kBAAW/R,GACT,OAAOyR,EACT,CACA,sBAAWxR,GACT,OAAO2R,EACT,CACA,eAAW/Y,GACT,MArCW,WAsCb,CAGA,QAAAmZ,GACM1V,KAAKwV,YAGLxV,KAAK6E,QAAQuQ,WACfpV,KAAK6E,QAAQwQ,YAAY5C,QAE3BlS,GAAaC,IAAInb,SAAU0vB,IAC3BxU,GAAac,GAAGhc,SAAU2vB,IAAiB5V,GAASY,KAAK2V,eAAevW,KACxEmB,GAAac,GAAGhc,SAAU4vB,IAAmB7V,GAASY,KAAK4V,eAAexW,KAC1EY,KAAKwV,WAAY,EACnB,CACA,UAAAK,GACO7V,KAAKwV,YAGVxV,KAAKwV,WAAY,EACjBjV,GAAaC,IAAInb,SAAU0vB,IAC7B,CAGA,cAAAY,CAAevW,GACb,MAAM,YACJiW,GACErV,KAAK6E,QACT,GAAIzF,EAAM7S,SAAWlH,UAAY+Z,EAAM7S,SAAW8oB,GAAeA,EAAY7wB,SAAS4a,EAAM7S,QAC1F,OAEF,MAAM1L,EAAWglB,GAAeU,kBAAkB8O,GAC1B,IAApBx0B,EAAS6P,OACX2kB,EAAY5C,QACHzS,KAAKyV,uBAAyBP,GACvCr0B,EAASA,EAAS6P,OAAS,GAAG+hB,QAE9B5xB,EAAS,GAAG4xB,OAEhB,CACA,cAAAmD,CAAexW,GAzED,QA0ERA,EAAMtiB,MAGVkjB,KAAKyV,qBAAuBrW,EAAM0W,SAAWZ,GA5EzB,UA6EtB,EAeF,MAAMa,GAAyB,oDACzBC,GAA0B,cAC1BC,GAAmB,gBACnBC,GAAkB,eAMxB,MAAMC,GACJ,WAAAhS,GACEnE,KAAK4E,SAAWvf,SAAS6G,IAC3B,CAGA,QAAAkqB,GAEE,MAAMC,EAAgBhxB,SAASC,gBAAgBuC,YAC/C,OAAO1F,KAAKoC,IAAI3E,OAAO02B,WAAaD,EACtC,CACA,IAAAzG,GACE,MAAM/rB,EAAQmc,KAAKoW,WACnBpW,KAAKuW,mBAELvW,KAAKwW,sBAAsBxW,KAAK4E,SAAUqR,IAAkBQ,GAAmBA,EAAkB5yB,IAEjGmc,KAAKwW,sBAAsBT,GAAwBE,IAAkBQ,GAAmBA,EAAkB5yB,IAC1Gmc,KAAKwW,sBAAsBR,GAAyBE,IAAiBO,GAAmBA,EAAkB5yB,GAC5G,CACA,KAAAwO,GACE2N,KAAK0W,wBAAwB1W,KAAK4E,SAAU,YAC5C5E,KAAK0W,wBAAwB1W,KAAK4E,SAAUqR,IAC5CjW,KAAK0W,wBAAwBX,GAAwBE,IACrDjW,KAAK0W,wBAAwBV,GAAyBE,GACxD,CACA,aAAAS,GACE,OAAO3W,KAAKoW,WAAa,CAC3B,CAGA,gBAAAG,GACEvW,KAAK4W,sBAAsB5W,KAAK4E,SAAU,YAC1C5E,KAAK4E,SAAS7jB,MAAM+K,SAAW,QACjC,CACA,qBAAA0qB,CAAsBzc,EAAU8c,EAAexa,GAC7C,MAAMya,EAAiB9W,KAAKoW,WAS5BpW,KAAK+W,2BAA2Bhd,GARHxa,IAC3B,GAAIA,IAAYygB,KAAK4E,UAAYhlB,OAAO02B,WAAa/2B,EAAQsI,YAAcivB,EACzE,OAEF9W,KAAK4W,sBAAsBr3B,EAASs3B,GACpC,MAAMJ,EAAkB72B,OAAOqF,iBAAiB1F,GAASub,iBAAiB+b,GAC1Et3B,EAAQwB,MAAMi2B,YAAYH,EAAe,GAAGxa,EAASkB,OAAOC,WAAWiZ,QAAsB,GAGjG,CACA,qBAAAG,CAAsBr3B,EAASs3B,GAC7B,MAAMI,EAAc13B,EAAQwB,MAAM+Z,iBAAiB+b,GAC/CI,GACFjU,GAAYC,iBAAiB1jB,EAASs3B,EAAeI,EAEzD,CACA,uBAAAP,CAAwB3c,EAAU8c,GAWhC7W,KAAK+W,2BAA2Bhd,GAVHxa,IAC3B,MAAM5B,EAAQqlB,GAAYQ,iBAAiBjkB,EAASs3B,GAEtC,OAAVl5B,GAIJqlB,GAAYE,oBAAoB3jB,EAASs3B,GACzCt3B,EAAQwB,MAAMi2B,YAAYH,EAAel5B,IAJvC4B,EAAQwB,MAAMm2B,eAAeL,EAIgB,GAGnD,CACA,0BAAAE,CAA2Bhd,EAAUod,GACnC,GAAI,GAAUpd,GACZod,EAASpd,QAGX,IAAK,MAAM6L,KAAOC,GAAe1T,KAAK4H,EAAUiG,KAAK4E,UACnDuS,EAASvR,EAEb,EAeF,MAEMwR,GAAc,YAGdC,GAAe,OAAOD,KACtBE,GAAyB,gBAAgBF,KACzCG,GAAiB,SAASH,KAC1BI,GAAe,OAAOJ,KACtBK,GAAgB,QAAQL,KACxBM,GAAiB,SAASN,KAC1BO,GAAsB,gBAAgBP,KACtCQ,GAA0B,oBAAoBR,KAC9CS,GAA0B,kBAAkBT,KAC5CU,GAAyB,QAAQV,cACjCW,GAAkB,aAElBC,GAAoB,OACpBC,GAAoB,eAKpBC,GAAY,CAChBtD,UAAU,EACVnC,OAAO,EACPzH,UAAU,GAENmN,GAAgB,CACpBvD,SAAU,mBACVnC,MAAO,UACPzH,SAAU,WAOZ,MAAMoN,WAAc1T,GAClB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAKqY,QAAUxS,GAAeC,QArBV,gBAqBmC9F,KAAK4E,UAC5D5E,KAAKsY,UAAYtY,KAAKuY,sBACtBvY,KAAKwY,WAAaxY,KAAKyY,uBACvBzY,KAAK2P,UAAW,EAChB3P,KAAKmP,kBAAmB,EACxBnP,KAAK0Y,WAAa,IAAIvC,GACtBnW,KAAK6L,oBACP,CAGA,kBAAWnI,GACT,OAAOwU,EACT,CACA,sBAAWvU,GACT,OAAOwU,EACT,CACA,eAAW5b,GACT,MA1DW,OA2Db,CAGA,MAAAoL,CAAO7H,GACL,OAAOE,KAAK2P,SAAW3P,KAAK4P,OAAS5P,KAAK6P,KAAK/P,EACjD,CACA,IAAA+P,CAAK/P,GACCE,KAAK2P,UAAY3P,KAAKmP,kBAGR5O,GAAaqB,QAAQ5B,KAAK4E,SAAU4S,GAAc,CAClE1X,kBAEYkC,mBAGdhC,KAAK2P,UAAW,EAChB3P,KAAKmP,kBAAmB,EACxBnP,KAAK0Y,WAAW9I,OAChBvqB,SAAS6G,KAAKmP,UAAU5E,IAAIshB,IAC5B/X,KAAK2Y,gBACL3Y,KAAKsY,UAAUzI,MAAK,IAAM7P,KAAK4Y,aAAa9Y,KAC9C,CACA,IAAA8P,GACO5P,KAAK2P,WAAY3P,KAAKmP,mBAGT5O,GAAaqB,QAAQ5B,KAAK4E,SAAUyS,IACxCrV,mBAGdhC,KAAK2P,UAAW,EAChB3P,KAAKmP,kBAAmB,EACxBnP,KAAKwY,WAAW3C,aAChB7V,KAAK4E,SAASvJ,UAAU1B,OAAOqe,IAC/BhY,KAAKmF,gBAAe,IAAMnF,KAAK6Y,cAAc7Y,KAAK4E,SAAU5E,KAAKgO,gBACnE,CACA,OAAAjJ,GACExE,GAAaC,IAAI5gB,OAAQw3B,IACzB7W,GAAaC,IAAIR,KAAKqY,QAASjB,IAC/BpX,KAAKsY,UAAUvT,UACf/E,KAAKwY,WAAW3C,aAChBlR,MAAMI,SACR,CACA,YAAA+T,GACE9Y,KAAK2Y,eACP,CAGA,mBAAAJ,GACE,OAAO,IAAIhE,GAAS,CAClB5Z,UAAWmG,QAAQd,KAAK6E,QAAQ+P,UAEhCxP,WAAYpF,KAAKgO,eAErB,CACA,oBAAAyK,GACE,OAAO,IAAIlD,GAAU,CACnBF,YAAarV,KAAK4E,UAEtB,CACA,YAAAgU,CAAa9Y,GAENza,SAAS6G,KAAK1H,SAASwb,KAAK4E,WAC/Bvf,SAAS6G,KAAK4oB,OAAO9U,KAAK4E,UAE5B5E,KAAK4E,SAAS7jB,MAAMgxB,QAAU,QAC9B/R,KAAK4E,SAASzjB,gBAAgB,eAC9B6e,KAAK4E,SAASxjB,aAAa,cAAc,GACzC4e,KAAK4E,SAASxjB,aAAa,OAAQ,UACnC4e,KAAK4E,SAASnZ,UAAY,EAC1B,MAAMstB,EAAYlT,GAAeC,QA7GT,cA6GsC9F,KAAKqY,SAC/DU,IACFA,EAAUttB,UAAY,GAExBoQ,GAAOmE,KAAK4E,UACZ5E,KAAK4E,SAASvJ,UAAU5E,IAAIuhB,IAU5BhY,KAAKmF,gBATsB,KACrBnF,KAAK6E,QAAQ4N,OACfzS,KAAKwY,WAAW9C,WAElB1V,KAAKmP,kBAAmB,EACxB5O,GAAaqB,QAAQ5B,KAAK4E,SAAU6S,GAAe,CACjD3X,iBACA,GAEoCE,KAAKqY,QAASrY,KAAKgO,cAC7D,CACA,kBAAAnC,GACEtL,GAAac,GAAGrB,KAAK4E,SAAUiT,IAAyBzY,IAhJvC,WAiJXA,EAAMtiB,MAGNkjB,KAAK6E,QAAQmG,SACfhL,KAAK4P,OAGP5P,KAAKgZ,6BAA4B,IAEnCzY,GAAac,GAAGzhB,OAAQ83B,IAAgB,KAClC1X,KAAK2P,WAAa3P,KAAKmP,kBACzBnP,KAAK2Y,eACP,IAEFpY,GAAac,GAAGrB,KAAK4E,SAAUgT,IAAyBxY,IAEtDmB,GAAae,IAAItB,KAAK4E,SAAU+S,IAAqBsB,IAC/CjZ,KAAK4E,WAAaxF,EAAM7S,QAAUyT,KAAK4E,WAAaqU,EAAO1sB,SAGjC,WAA1ByT,KAAK6E,QAAQ+P,SAIb5U,KAAK6E,QAAQ+P,UACf5U,KAAK4P,OAJL5P,KAAKgZ,6BAKP,GACA,GAEN,CACA,UAAAH,GACE7Y,KAAK4E,SAAS7jB,MAAMgxB,QAAU,OAC9B/R,KAAK4E,SAASxjB,aAAa,eAAe,GAC1C4e,KAAK4E,SAASzjB,gBAAgB,cAC9B6e,KAAK4E,SAASzjB,gBAAgB,QAC9B6e,KAAKmP,kBAAmB,EACxBnP,KAAKsY,UAAU1I,MAAK,KAClBvqB,SAAS6G,KAAKmP,UAAU1B,OAAOoe,IAC/B/X,KAAKkZ,oBACLlZ,KAAK0Y,WAAWrmB,QAChBkO,GAAaqB,QAAQ5B,KAAK4E,SAAU2S,GAAe,GAEvD,CACA,WAAAvJ,GACE,OAAOhO,KAAK4E,SAASvJ,UAAU7W,SAjLT,OAkLxB,CACA,0BAAAw0B,GAEE,GADkBzY,GAAaqB,QAAQ5B,KAAK4E,SAAU0S,IACxCtV,iBACZ,OAEF,MAAMmX,EAAqBnZ,KAAK4E,SAASvX,aAAehI,SAASC,gBAAgBsC,aAC3EwxB,EAAmBpZ,KAAK4E,SAAS7jB,MAAMiL,UAEpB,WAArBotB,GAAiCpZ,KAAK4E,SAASvJ,UAAU7W,SAASyzB,MAGjEkB,IACHnZ,KAAK4E,SAAS7jB,MAAMiL,UAAY,UAElCgU,KAAK4E,SAASvJ,UAAU5E,IAAIwhB,IAC5BjY,KAAKmF,gBAAe,KAClBnF,KAAK4E,SAASvJ,UAAU1B,OAAOse,IAC/BjY,KAAKmF,gBAAe,KAClBnF,KAAK4E,SAAS7jB,MAAMiL,UAAYotB,CAAgB,GAC/CpZ,KAAKqY,QAAQ,GACfrY,KAAKqY,SACRrY,KAAK4E,SAAS6N,QAChB,CAMA,aAAAkG,GACE,MAAMQ,EAAqBnZ,KAAK4E,SAASvX,aAAehI,SAASC,gBAAgBsC,aAC3EkvB,EAAiB9W,KAAK0Y,WAAWtC,WACjCiD,EAAoBvC,EAAiB,EAC3C,GAAIuC,IAAsBF,EAAoB,CAC5C,MAAMr3B,EAAWma,KAAU,cAAgB,eAC3C+D,KAAK4E,SAAS7jB,MAAMe,GAAY,GAAGg1B,KACrC,CACA,IAAKuC,GAAqBF,EAAoB,CAC5C,MAAMr3B,EAAWma,KAAU,eAAiB,cAC5C+D,KAAK4E,SAAS7jB,MAAMe,GAAY,GAAGg1B,KACrC,CACF,CACA,iBAAAoC,GACElZ,KAAK4E,SAAS7jB,MAAMu4B,YAAc,GAClCtZ,KAAK4E,SAAS7jB,MAAMw4B,aAAe,EACrC,CAGA,sBAAO9c,CAAgBqH,EAAQhE,GAC7B,OAAOE,KAAKwH,MAAK,WACf,MAAMnd,EAAO+tB,GAAM9S,oBAAoBtF,KAAM8D,GAC7C,GAAsB,iBAAXA,EAAX,CAGA,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,GAAQhE,EAJb,CAKF,GACF,EAOFS,GAAac,GAAGhc,SAAUyyB,GA9OK,4BA8O2C,SAAU1Y,GAClF,MAAM7S,EAASsZ,GAAec,uBAAuB3G,MACjD,CAAC,IAAK,QAAQoB,SAASpB,KAAKiH,UAC9B7H,EAAMkD,iBAER/B,GAAae,IAAI/U,EAAQirB,IAAcgC,IACjCA,EAAUxX,kBAIdzB,GAAae,IAAI/U,EAAQgrB,IAAgB,KACnC5c,GAAUqF,OACZA,KAAKyS,OACP,GACA,IAIJ,MAAMgH,EAAc5T,GAAeC,QAnQb,eAoQlB2T,GACFrB,GAAM/S,YAAYoU,GAAa7J,OAEpBwI,GAAM9S,oBAAoB/Y,GAClCob,OAAO3H,KACd,IACA6G,GAAqBuR,IAMrBjc,GAAmBic,IAcnB,MAEMsB,GAAc,gBACdC,GAAiB,YACjBC,GAAwB,OAAOF,KAAcC,KAE7CE,GAAoB,OACpBC,GAAuB,UACvBC,GAAoB,SAEpBC,GAAgB,kBAChBC,GAAe,OAAOP,KACtBQ,GAAgB,QAAQR,KACxBS,GAAe,OAAOT,KACtBU,GAAuB,gBAAgBV,KACvCW,GAAiB,SAASX,KAC1BY,GAAe,SAASZ,KACxBa,GAAyB,QAAQb,KAAcC,KAC/Ca,GAAwB,kBAAkBd,KAE1Ce,GAAY,CAChB7F,UAAU,EACV5J,UAAU,EACVvgB,QAAQ,GAEJiwB,GAAgB,CACpB9F,SAAU,mBACV5J,SAAU,UACVvgB,OAAQ,WAOV,MAAMkwB,WAAkBjW,GACtB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAK2P,UAAW,EAChB3P,KAAKsY,UAAYtY,KAAKuY,sBACtBvY,KAAKwY,WAAaxY,KAAKyY,uBACvBzY,KAAK6L,oBACP,CAGA,kBAAWnI,GACT,OAAO+W,EACT,CACA,sBAAW9W,GACT,OAAO+W,EACT,CACA,eAAWne,GACT,MApDW,WAqDb,CAGA,MAAAoL,CAAO7H,GACL,OAAOE,KAAK2P,SAAW3P,KAAK4P,OAAS5P,KAAK6P,KAAK/P,EACjD,CACA,IAAA+P,CAAK/P,GACCE,KAAK2P,UAGSpP,GAAaqB,QAAQ5B,KAAK4E,SAAUqV,GAAc,CAClEna,kBAEYkC,mBAGdhC,KAAK2P,UAAW,EAChB3P,KAAKsY,UAAUzI,OACV7P,KAAK6E,QAAQpa,SAChB,IAAI0rB,IAAkBvG,OAExB5P,KAAK4E,SAASxjB,aAAa,cAAc,GACzC4e,KAAK4E,SAASxjB,aAAa,OAAQ,UACnC4e,KAAK4E,SAASvJ,UAAU5E,IAAIqjB,IAW5B9Z,KAAKmF,gBAVoB,KAClBnF,KAAK6E,QAAQpa,SAAUuV,KAAK6E,QAAQ+P,UACvC5U,KAAKwY,WAAW9C,WAElB1V,KAAK4E,SAASvJ,UAAU5E,IAAIojB,IAC5B7Z,KAAK4E,SAASvJ,UAAU1B,OAAOmgB,IAC/BvZ,GAAaqB,QAAQ5B,KAAK4E,SAAUsV,GAAe,CACjDpa,iBACA,GAEkCE,KAAK4E,UAAU,GACvD,CACA,IAAAgL,GACO5P,KAAK2P,WAGQpP,GAAaqB,QAAQ5B,KAAK4E,SAAUuV,IACxCnY,mBAGdhC,KAAKwY,WAAW3C,aAChB7V,KAAK4E,SAASgW,OACd5a,KAAK2P,UAAW,EAChB3P,KAAK4E,SAASvJ,UAAU5E,IAAIsjB,IAC5B/Z,KAAKsY,UAAU1I,OAUf5P,KAAKmF,gBAToB,KACvBnF,KAAK4E,SAASvJ,UAAU1B,OAAOkgB,GAAmBE,IAClD/Z,KAAK4E,SAASzjB,gBAAgB,cAC9B6e,KAAK4E,SAASzjB,gBAAgB,QACzB6e,KAAK6E,QAAQpa,SAChB,IAAI0rB,IAAkB9jB,QAExBkO,GAAaqB,QAAQ5B,KAAK4E,SAAUyV,GAAe,GAEfra,KAAK4E,UAAU,IACvD,CACA,OAAAG,GACE/E,KAAKsY,UAAUvT,UACf/E,KAAKwY,WAAW3C,aAChBlR,MAAMI,SACR,CAGA,mBAAAwT,GACE,MASM5d,EAAYmG,QAAQd,KAAK6E,QAAQ+P,UACvC,OAAO,IAAIL,GAAS,CAClBJ,UA3HsB,qBA4HtBxZ,YACAyK,YAAY,EACZiP,YAAarU,KAAK4E,SAAS7f,WAC3BqvB,cAAezZ,EAfK,KACU,WAA1BqF,KAAK6E,QAAQ+P,SAIjB5U,KAAK4P,OAHHrP,GAAaqB,QAAQ5B,KAAK4E,SAAUwV,GAG3B,EAUgC,MAE/C,CACA,oBAAA3B,GACE,OAAO,IAAIlD,GAAU,CACnBF,YAAarV,KAAK4E,UAEtB,CACA,kBAAAiH,GACEtL,GAAac,GAAGrB,KAAK4E,SAAU4V,IAAuBpb,IA5IvC,WA6ITA,EAAMtiB,MAGNkjB,KAAK6E,QAAQmG,SACfhL,KAAK4P,OAGPrP,GAAaqB,QAAQ5B,KAAK4E,SAAUwV,IAAqB,GAE7D,CAGA,sBAAO3d,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOswB,GAAUrV,oBAAoBtF,KAAM8D,GACjD,GAAsB,iBAAXA,EAAX,CAGA,QAAqB/K,IAAjB1O,EAAKyZ,IAAyBA,EAAOrC,WAAW,MAAmB,gBAAXqC,EAC1D,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,GAAQ9D,KAJb,CAKF,GACF,EAOFO,GAAac,GAAGhc,SAAUk1B,GA7JK,gCA6J2C,SAAUnb,GAClF,MAAM7S,EAASsZ,GAAec,uBAAuB3G,MAIrD,GAHI,CAAC,IAAK,QAAQoB,SAASpB,KAAKiH,UAC9B7H,EAAMkD,iBAEJpH,GAAW8E,MACb,OAEFO,GAAae,IAAI/U,EAAQ8tB,IAAgB,KAEnC1f,GAAUqF,OACZA,KAAKyS,OACP,IAIF,MAAMgH,EAAc5T,GAAeC,QAAQkU,IACvCP,GAAeA,IAAgBltB,GACjCouB,GAAUtV,YAAYoU,GAAa7J,OAExB+K,GAAUrV,oBAAoB/Y,GACtCob,OAAO3H,KACd,IACAO,GAAac,GAAGzhB,OAAQg6B,IAAuB,KAC7C,IAAK,MAAM7f,KAAY8L,GAAe1T,KAAK6nB,IACzCW,GAAUrV,oBAAoBvL,GAAU8V,MAC1C,IAEFtP,GAAac,GAAGzhB,OAAQ06B,IAAc,KACpC,IAAK,MAAM/6B,KAAWsmB,GAAe1T,KAAK,gDACG,UAAvClN,iBAAiB1F,GAASiC,UAC5Bm5B,GAAUrV,oBAAoB/lB,GAASqwB,MAE3C,IAEF/I,GAAqB8T,IAMrBxe,GAAmBwe,IAUnB,MACME,GAAmB,CAEvB,IAAK,CAAC,QAAS,MAAO,KAAM,OAAQ,OAHP,kBAI7BhqB,EAAG,CAAC,SAAU,OAAQ,QAAS,OAC/BiqB,KAAM,GACNhqB,EAAG,GACHiqB,GAAI,GACJC,IAAK,GACLC,KAAM,GACNC,GAAI,GACJC,IAAK,GACLC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJC,GAAI,GACJxqB,EAAG,GACH0b,IAAK,CAAC,MAAO,SAAU,MAAO,QAAS,QAAS,UAChD+O,GAAI,GACJC,GAAI,GACJC,EAAG,GACHC,IAAK,GACLC,EAAG,GACHC,MAAO,GACPC,KAAM,GACNC,IAAK,GACLC,IAAK,GACLC,OAAQ,GACRC,EAAG,GACHC,GAAI,IAIAC,GAAgB,IAAIpmB,IAAI,CAAC,aAAc,OAAQ,OAAQ,WAAY,WAAY,SAAU,MAAO,eAShGqmB,GAAmB,0DACnBC,GAAmB,CAAC76B,EAAW86B,KACnC,MAAMC,EAAgB/6B,EAAUvC,SAASC,cACzC,OAAIo9B,EAAqBzb,SAAS0b,IAC5BJ,GAAc/lB,IAAImmB,IACbhc,QAAQ6b,GAAiBt5B,KAAKtB,EAAUg7B,YAM5CF,EAAqB12B,QAAO62B,GAAkBA,aAA0BzY,SAAQ9R,MAAKwqB,GAASA,EAAM55B,KAAKy5B,IAAe,EA0C3HI,GAAY,CAChBC,UAAWtC,GACXuC,QAAS,CAAC,EAEVC,WAAY,GACZxwB,MAAM,EACNywB,UAAU,EACVC,WAAY,KACZC,SAAU,eAENC,GAAgB,CACpBN,UAAW,SACXC,QAAS,SACTC,WAAY,oBACZxwB,KAAM,UACNywB,SAAU,UACVC,WAAY,kBACZC,SAAU,UAENE,GAAqB,CACzBC,MAAO,iCACP5jB,SAAU,oBAOZ,MAAM6jB,WAAwBna,GAC5B,WAAAU,CAAYL,GACVa,QACA3E,KAAK6E,QAAU7E,KAAK6D,WAAWC,EACjC,CAGA,kBAAWJ,GACT,OAAOwZ,EACT,CACA,sBAAWvZ,GACT,OAAO8Z,EACT,CACA,eAAWlhB,GACT,MA3CW,iBA4Cb,CAGA,UAAAshB,GACE,OAAO7gC,OAAOmiB,OAAOa,KAAK6E,QAAQuY,SAASt6B,KAAIghB,GAAU9D,KAAK8d,yBAAyBha,KAAS3d,OAAO2a,QACzG,CACA,UAAAid,GACE,OAAO/d,KAAK6d,aAAantB,OAAS,CACpC,CACA,aAAAstB,CAAcZ,GAMZ,OALApd,KAAKie,cAAcb,GACnBpd,KAAK6E,QAAQuY,QAAU,IAClBpd,KAAK6E,QAAQuY,WACbA,GAEEpd,IACT,CACA,MAAAke,GACE,MAAMC,EAAkB94B,SAASwvB,cAAc,OAC/CsJ,EAAgBC,UAAYpe,KAAKqe,eAAere,KAAK6E,QAAQ2Y,UAC7D,IAAK,MAAOzjB,EAAUukB,KAASthC,OAAOmkB,QAAQnB,KAAK6E,QAAQuY,SACzDpd,KAAKue,YAAYJ,EAAiBG,EAAMvkB,GAE1C,MAAMyjB,EAAWW,EAAgBpY,SAAS,GACpCsX,EAAard,KAAK8d,yBAAyB9d,KAAK6E,QAAQwY,YAI9D,OAHIA,GACFG,EAASniB,UAAU5E,OAAO4mB,EAAWn7B,MAAM,MAEtCs7B,CACT,CAGA,gBAAAvZ,CAAiBH,GACfa,MAAMV,iBAAiBH,GACvB9D,KAAKie,cAAcna,EAAOsZ,QAC5B,CACA,aAAAa,CAAcO,GACZ,IAAK,MAAOzkB,EAAUqjB,KAAYpgC,OAAOmkB,QAAQqd,GAC/C7Z,MAAMV,iBAAiB,CACrBlK,WACA4jB,MAAOP,GACNM,GAEP,CACA,WAAAa,CAAYf,EAAUJ,EAASrjB,GAC7B,MAAM0kB,EAAkB5Y,GAAeC,QAAQ/L,EAAUyjB,GACpDiB,KAGLrB,EAAUpd,KAAK8d,yBAAyBV,IAKpC,GAAUA,GACZpd,KAAK0e,sBAAsBhkB,GAAW0iB,GAAUqB,GAG9Cze,KAAK6E,QAAQhY,KACf4xB,EAAgBL,UAAYpe,KAAKqe,eAAejB,GAGlDqB,EAAgBE,YAAcvB,EAX5BqB,EAAgB9kB,SAYpB,CACA,cAAA0kB,CAAeG,GACb,OAAOxe,KAAK6E,QAAQyY,SApJxB,SAAsBsB,EAAYzB,EAAW0B,GAC3C,IAAKD,EAAWluB,OACd,OAAOkuB,EAET,GAAIC,GAAgD,mBAArBA,EAC7B,OAAOA,EAAiBD,GAE1B,MACME,GADY,IAAIl/B,OAAOm/B,WACKC,gBAAgBJ,EAAY,aACxD/9B,EAAW,GAAGlC,UAAUmgC,EAAgB5yB,KAAKkU,iBAAiB,MACpE,IAAK,MAAM7gB,KAAWsB,EAAU,CAC9B,MAAMo+B,EAAc1/B,EAAQC,SAASC,cACrC,IAAKzC,OAAO4D,KAAKu8B,GAAW/b,SAAS6d,GAAc,CACjD1/B,EAAQoa,SACR,QACF,CACA,MAAMulB,EAAgB,GAAGvgC,UAAUY,EAAQ0B,YACrCk+B,EAAoB,GAAGxgC,OAAOw+B,EAAU,MAAQ,GAAIA,EAAU8B,IAAgB,IACpF,IAAK,MAAMl9B,KAAam9B,EACjBtC,GAAiB76B,EAAWo9B,IAC/B5/B,EAAQ4B,gBAAgBY,EAAUvC,SAGxC,CACA,OAAOs/B,EAAgB5yB,KAAKkyB,SAC9B,CA2HmCgB,CAAaZ,EAAKxe,KAAK6E,QAAQsY,UAAWnd,KAAK6E,QAAQ0Y,YAAciB,CACtG,CACA,wBAAAV,CAAyBU,GACvB,OAAO3hB,GAAQ2hB,EAAK,CAACxe,MACvB,CACA,qBAAA0e,CAAsBn/B,EAASk/B,GAC7B,GAAIze,KAAK6E,QAAQhY,KAGf,OAFA4xB,EAAgBL,UAAY,QAC5BK,EAAgB3J,OAAOv1B,GAGzBk/B,EAAgBE,YAAcp/B,EAAQo/B,WACxC,EAeF,MACMU,GAAwB,IAAI/oB,IAAI,CAAC,WAAY,YAAa,eAC1DgpB,GAAoB,OAEpBC,GAAoB,OACpBC,GAAyB,iBACzBC,GAAiB,SACjBC,GAAmB,gBACnBC,GAAgB,QAChBC,GAAgB,QAahBC,GAAgB,CACpBC,KAAM,OACNC,IAAK,MACLC,MAAO/jB,KAAU,OAAS,QAC1BgkB,OAAQ,SACRC,KAAMjkB,KAAU,QAAU,QAEtBkkB,GAAY,CAChBhD,UAAWtC,GACXuF,WAAW,EACXnyB,SAAU,kBACVoyB,WAAW,EACXC,YAAa,GACbC,MAAO,EACPvwB,mBAAoB,CAAC,MAAO,QAAS,SAAU,QAC/CnD,MAAM,EACN7E,OAAQ,CAAC,EAAG,GACZtJ,UAAW,MACXszB,aAAc,KACdsL,UAAU,EACVC,WAAY,KACZxjB,UAAU,EACVyjB,SAAU,+GACVgD,MAAO,GACP5e,QAAS,eAEL6e,GAAgB,CACpBtD,UAAW,SACXiD,UAAW,UACXnyB,SAAU,mBACVoyB,UAAW,2BACXC,YAAa,oBACbC,MAAO,kBACPvwB,mBAAoB,QACpBnD,KAAM,UACN7E,OAAQ,0BACRtJ,UAAW,oBACXszB,aAAc,yBACdsL,SAAU,UACVC,WAAY,kBACZxjB,SAAU,mBACVyjB,SAAU,SACVgD,MAAO,4BACP5e,QAAS,UAOX,MAAM8e,WAAgBhc,GACpB,WAAAP,CAAY5kB,EAASukB,GACnB,QAAsB,IAAX,EACT,MAAM,IAAIU,UAAU,+DAEtBG,MAAMplB,EAASukB,GAGf9D,KAAK2gB,YAAa,EAClB3gB,KAAK4gB,SAAW,EAChB5gB,KAAK6gB,WAAa,KAClB7gB,KAAK8gB,eAAiB,CAAC,EACvB9gB,KAAKmS,QAAU,KACfnS,KAAK+gB,iBAAmB,KACxB/gB,KAAKghB,YAAc,KAGnBhhB,KAAKihB,IAAM,KACXjhB,KAAKkhB,gBACAlhB,KAAK6E,QAAQ9K,UAChBiG,KAAKmhB,WAET,CAGA,kBAAWzd,GACT,OAAOyc,EACT,CACA,sBAAWxc,GACT,OAAO8c,EACT,CACA,eAAWlkB,GACT,MAxGW,SAyGb,CAGA,MAAA6kB,GACEphB,KAAK2gB,YAAa,CACpB,CACA,OAAAU,GACErhB,KAAK2gB,YAAa,CACpB,CACA,aAAAW,GACEthB,KAAK2gB,YAAc3gB,KAAK2gB,UAC1B,CACA,MAAAhZ,GACO3H,KAAK2gB,aAGV3gB,KAAK8gB,eAAeS,OAASvhB,KAAK8gB,eAAeS,MAC7CvhB,KAAK2P,WACP3P,KAAKwhB,SAGPxhB,KAAKyhB,SACP,CACA,OAAA1c,GACEmI,aAAalN,KAAK4gB,UAClBrgB,GAAaC,IAAIR,KAAK4E,SAAS5J,QAAQykB,IAAiBC,GAAkB1f,KAAK0hB,mBAC3E1hB,KAAK4E,SAASpJ,aAAa,2BAC7BwE,KAAK4E,SAASxjB,aAAa,QAAS4e,KAAK4E,SAASpJ,aAAa,2BAEjEwE,KAAK2hB,iBACLhd,MAAMI,SACR,CACA,IAAA8K,GACE,GAAoC,SAAhC7P,KAAK4E,SAAS7jB,MAAMgxB,QACtB,MAAM,IAAInO,MAAM,uCAElB,IAAM5D,KAAK4hB,mBAAoB5hB,KAAK2gB,WAClC,OAEF,MAAMnH,EAAYjZ,GAAaqB,QAAQ5B,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UAlItD,SAoIXqc,GADapmB,GAAeuE,KAAK4E,WACL5E,KAAK4E,SAAS9kB,cAAcwF,iBAAiBd,SAASwb,KAAK4E,UAC7F,GAAI4U,EAAUxX,mBAAqB6f,EACjC,OAIF7hB,KAAK2hB,iBACL,MAAMV,EAAMjhB,KAAK8hB,iBACjB9hB,KAAK4E,SAASxjB,aAAa,mBAAoB6/B,EAAIzlB,aAAa,OAChE,MAAM,UACJ6kB,GACErgB,KAAK6E,QAYT,GAXK7E,KAAK4E,SAAS9kB,cAAcwF,gBAAgBd,SAASwb,KAAKihB,OAC7DZ,EAAUvL,OAAOmM,GACjB1gB,GAAaqB,QAAQ5B,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UAhJpC,cAkJnBxF,KAAKmS,QAAUnS,KAAKwS,cAAcyO,GAClCA,EAAI5lB,UAAU5E,IAAI8oB,IAMd,iBAAkBl6B,SAASC,gBAC7B,IAAK,MAAM/F,IAAW,GAAGZ,UAAU0G,SAAS6G,KAAK6Z,UAC/CxF,GAAac,GAAG9hB,EAAS,YAAaqc,IAU1CoE,KAAKmF,gBAPY,KACf5E,GAAaqB,QAAQ5B,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UAhKrC,WAiKQ,IAApBxF,KAAK6gB,YACP7gB,KAAKwhB,SAEPxhB,KAAK6gB,YAAa,CAAK,GAEK7gB,KAAKihB,IAAKjhB,KAAKgO,cAC/C,CACA,IAAA4B,GACE,GAAK5P,KAAK2P,aAGQpP,GAAaqB,QAAQ5B,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UA/KtD,SAgLHxD,iBAAd,CAQA,GALYhC,KAAK8hB,iBACbzmB,UAAU1B,OAAO4lB,IAIjB,iBAAkBl6B,SAASC,gBAC7B,IAAK,MAAM/F,IAAW,GAAGZ,UAAU0G,SAAS6G,KAAK6Z,UAC/CxF,GAAaC,IAAIjhB,EAAS,YAAaqc,IAG3CoE,KAAK8gB,eAA4B,OAAI,EACrC9gB,KAAK8gB,eAAelB,KAAiB,EACrC5f,KAAK8gB,eAAenB,KAAiB,EACrC3f,KAAK6gB,WAAa,KAYlB7gB,KAAKmF,gBAVY,KACXnF,KAAK+hB,yBAGJ/hB,KAAK6gB,YACR7gB,KAAK2hB,iBAEP3hB,KAAK4E,SAASzjB,gBAAgB,oBAC9Bof,GAAaqB,QAAQ5B,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UAzMpC,WAyM8D,GAEnDxF,KAAKihB,IAAKjhB,KAAKgO,cA1B7C,CA2BF,CACA,MAAAjjB,GACMiV,KAAKmS,SACPnS,KAAKmS,QAAQpnB,QAEjB,CAGA,cAAA62B,GACE,OAAO9gB,QAAQd,KAAKgiB,YACtB,CACA,cAAAF,GAIE,OAHK9hB,KAAKihB,MACRjhB,KAAKihB,IAAMjhB,KAAKiiB,kBAAkBjiB,KAAKghB,aAAehhB,KAAKkiB,2BAEtDliB,KAAKihB,GACd,CACA,iBAAAgB,CAAkB7E,GAChB,MAAM6D,EAAMjhB,KAAKmiB,oBAAoB/E,GAASc,SAG9C,IAAK+C,EACH,OAAO,KAETA,EAAI5lB,UAAU1B,OAAO2lB,GAAmBC,IAExC0B,EAAI5lB,UAAU5E,IAAI,MAAMuJ,KAAKmE,YAAY5H,aACzC,MAAM6lB,EAvuGKC,KACb,GACEA,GAAUlgC,KAAKmgC,MA/BH,IA+BSngC,KAAKogC,gBACnBl9B,SAASm9B,eAAeH,IACjC,OAAOA,CAAM,EAmuGGI,CAAOziB,KAAKmE,YAAY5H,MAAM1c,WAK5C,OAJAohC,EAAI7/B,aAAa,KAAMghC,GACnBpiB,KAAKgO,eACPiT,EAAI5lB,UAAU5E,IAAI6oB,IAEb2B,CACT,CACA,UAAAyB,CAAWtF,GACTpd,KAAKghB,YAAc5D,EACfpd,KAAK2P,aACP3P,KAAK2hB,iBACL3hB,KAAK6P,OAET,CACA,mBAAAsS,CAAoB/E,GAYlB,OAXIpd,KAAK+gB,iBACP/gB,KAAK+gB,iBAAiB/C,cAAcZ,GAEpCpd,KAAK+gB,iBAAmB,IAAInD,GAAgB,IACvC5d,KAAK6E,QAGRuY,UACAC,WAAYrd,KAAK8d,yBAAyB9d,KAAK6E,QAAQyb,eAGpDtgB,KAAK+gB,gBACd,CACA,sBAAAmB,GACE,MAAO,CACL,CAAC1C,IAAyBxf,KAAKgiB,YAEnC,CACA,SAAAA,GACE,OAAOhiB,KAAK8d,yBAAyB9d,KAAK6E,QAAQ2b,QAAUxgB,KAAK4E,SAASpJ,aAAa,yBACzF,CAGA,4BAAAmnB,CAA6BvjB,GAC3B,OAAOY,KAAKmE,YAAYmB,oBAAoBlG,EAAMW,eAAgBC,KAAK4iB,qBACzE,CACA,WAAA5U,GACE,OAAOhO,KAAK6E,QAAQub,WAAapgB,KAAKihB,KAAOjhB,KAAKihB,IAAI5lB,UAAU7W,SAAS86B,GAC3E,CACA,QAAA3P,GACE,OAAO3P,KAAKihB,KAAOjhB,KAAKihB,IAAI5lB,UAAU7W,SAAS+6B,GACjD,CACA,aAAA/M,CAAcyO,GACZ,MAAMviC,EAAYme,GAAQmD,KAAK6E,QAAQnmB,UAAW,CAACshB,KAAMihB,EAAKjhB,KAAK4E,WAC7Die,EAAahD,GAAcnhC,EAAU+lB,eAC3C,OAAO,GAAoBzE,KAAK4E,SAAUqc,EAAKjhB,KAAK4S,iBAAiBiQ,GACvE,CACA,UAAA7P,GACE,MAAM,OACJhrB,GACEgY,KAAK6E,QACT,MAAsB,iBAAX7c,EACFA,EAAO9F,MAAM,KAAKY,KAAInF,GAAS4f,OAAOgQ,SAAS5vB,EAAO,MAEzC,mBAAXqK,EACFirB,GAAcjrB,EAAOirB,EAAYjT,KAAK4E,UAExC5c,CACT,CACA,wBAAA81B,CAAyBU,GACvB,OAAO3hB,GAAQ2hB,EAAK,CAACxe,KAAK4E,UAC5B,CACA,gBAAAgO,CAAiBiQ,GACf,MAAM3P,EAAwB,CAC5Bx0B,UAAWmkC,EACXzsB,UAAW,CAAC,CACV9V,KAAM,OACNmB,QAAS,CACPuO,mBAAoBgQ,KAAK6E,QAAQ7U,qBAElC,CACD1P,KAAM,SACNmB,QAAS,CACPuG,OAAQgY,KAAKgT,eAEd,CACD1yB,KAAM,kBACNmB,QAAS,CACPwM,SAAU+R,KAAK6E,QAAQ5W,WAExB,CACD3N,KAAM,QACNmB,QAAS,CACPlC,QAAS,IAAIygB,KAAKmE,YAAY5H,eAE/B,CACDjc,KAAM,kBACNC,SAAS,EACTC,MAAO,aACPC,GAAI4J,IAGF2V,KAAK8hB,iBAAiB1gC,aAAa,wBAAyBiJ,EAAK1J,MAAMjC,UAAU,KAIvF,MAAO,IACFw0B,KACArW,GAAQmD,KAAK6E,QAAQmN,aAAc,CAACkB,IAE3C,CACA,aAAAgO,GACE,MAAM4B,EAAW9iB,KAAK6E,QAAQjD,QAAQ1f,MAAM,KAC5C,IAAK,MAAM0f,KAAWkhB,EACpB,GAAgB,UAAZlhB,EACFrB,GAAac,GAAGrB,KAAK4E,SAAU5E,KAAKmE,YAAYqB,UAjVlC,SAiV4DxF,KAAK6E,QAAQ9K,UAAUqF,IAC/EY,KAAK2iB,6BAA6BvjB,GAC1CuI,QAAQ,SAEb,GA3VU,WA2VN/F,EAA4B,CACrC,MAAMmhB,EAAUnhB,IAAY+d,GAAgB3f,KAAKmE,YAAYqB,UAnV5C,cAmV0ExF,KAAKmE,YAAYqB,UArV5F,WAsVVwd,EAAWphB,IAAY+d,GAAgB3f,KAAKmE,YAAYqB,UAnV7C,cAmV2ExF,KAAKmE,YAAYqB,UArV5F,YAsVjBjF,GAAac,GAAGrB,KAAK4E,SAAUme,EAAS/iB,KAAK6E,QAAQ9K,UAAUqF,IAC7D,MAAMkU,EAAUtT,KAAK2iB,6BAA6BvjB,GAClDkU,EAAQwN,eAA8B,YAAf1hB,EAAMqB,KAAqBmf,GAAgBD,KAAiB,EACnFrM,EAAQmO,QAAQ,IAElBlhB,GAAac,GAAGrB,KAAK4E,SAAUoe,EAAUhjB,KAAK6E,QAAQ9K,UAAUqF,IAC9D,MAAMkU,EAAUtT,KAAK2iB,6BAA6BvjB,GAClDkU,EAAQwN,eAA8B,aAAf1hB,EAAMqB,KAAsBmf,GAAgBD,IAAiBrM,EAAQ1O,SAASpgB,SAAS4a,EAAMU,eACpHwT,EAAQkO,QAAQ,GAEpB,CAEFxhB,KAAK0hB,kBAAoB,KACnB1hB,KAAK4E,UACP5E,KAAK4P,MACP,EAEFrP,GAAac,GAAGrB,KAAK4E,SAAS5J,QAAQykB,IAAiBC,GAAkB1f,KAAK0hB,kBAChF,CACA,SAAAP,GACE,MAAMX,EAAQxgB,KAAK4E,SAASpJ,aAAa,SACpCglB,IAGAxgB,KAAK4E,SAASpJ,aAAa,eAAkBwE,KAAK4E,SAAS+Z,YAAYhZ,QAC1E3F,KAAK4E,SAASxjB,aAAa,aAAco/B,GAE3CxgB,KAAK4E,SAASxjB,aAAa,yBAA0Bo/B,GACrDxgB,KAAK4E,SAASzjB,gBAAgB,SAChC,CACA,MAAAsgC,GACMzhB,KAAK2P,YAAc3P,KAAK6gB,WAC1B7gB,KAAK6gB,YAAa,GAGpB7gB,KAAK6gB,YAAa,EAClB7gB,KAAKijB,aAAY,KACXjjB,KAAK6gB,YACP7gB,KAAK6P,MACP,GACC7P,KAAK6E,QAAQ0b,MAAM1Q,MACxB,CACA,MAAA2R,GACMxhB,KAAK+hB,yBAGT/hB,KAAK6gB,YAAa,EAClB7gB,KAAKijB,aAAY,KACVjjB,KAAK6gB,YACR7gB,KAAK4P,MACP,GACC5P,KAAK6E,QAAQ0b,MAAM3Q,MACxB,CACA,WAAAqT,CAAYrlB,EAASslB,GACnBhW,aAAalN,KAAK4gB,UAClB5gB,KAAK4gB,SAAW/iB,WAAWD,EAASslB,EACtC,CACA,oBAAAnB,GACE,OAAO/kC,OAAOmiB,OAAOa,KAAK8gB,gBAAgB1f,UAAS,EACrD,CACA,UAAAyC,CAAWC,GACT,MAAMqf,EAAiBngB,GAAYG,kBAAkBnD,KAAK4E,UAC1D,IAAK,MAAMwe,KAAiBpmC,OAAO4D,KAAKuiC,GAClC9D,GAAsB1oB,IAAIysB,WACrBD,EAAeC,GAU1B,OAPAtf,EAAS,IACJqf,KACmB,iBAAXrf,GAAuBA,EAASA,EAAS,CAAC,GAEvDA,EAAS9D,KAAK+D,gBAAgBD,GAC9BA,EAAS9D,KAAKgE,kBAAkBF,GAChC9D,KAAKiE,iBAAiBH,GACfA,CACT,CACA,iBAAAE,CAAkBF,GAchB,OAbAA,EAAOuc,WAAiC,IAArBvc,EAAOuc,UAAsBh7B,SAAS6G,KAAOwO,GAAWoJ,EAAOuc,WACtD,iBAAjBvc,EAAOyc,QAChBzc,EAAOyc,MAAQ,CACb1Q,KAAM/L,EAAOyc,MACb3Q,KAAM9L,EAAOyc,QAGW,iBAAjBzc,EAAO0c,QAChB1c,EAAO0c,MAAQ1c,EAAO0c,MAAM3gC,YAEA,iBAAnBikB,EAAOsZ,UAChBtZ,EAAOsZ,QAAUtZ,EAAOsZ,QAAQv9B,YAE3BikB,CACT,CACA,kBAAA8e,GACE,MAAM9e,EAAS,CAAC,EAChB,IAAK,MAAOhnB,EAAKa,KAAUX,OAAOmkB,QAAQnB,KAAK6E,SACzC7E,KAAKmE,YAAYT,QAAQ5mB,KAASa,IACpCmmB,EAAOhnB,GAAOa,GASlB,OANAmmB,EAAO/J,UAAW,EAClB+J,EAAOlC,QAAU,SAKVkC,CACT,CACA,cAAA6d,GACM3hB,KAAKmS,UACPnS,KAAKmS,QAAQnZ,UACbgH,KAAKmS,QAAU,MAEbnS,KAAKihB,MACPjhB,KAAKihB,IAAItnB,SACTqG,KAAKihB,IAAM,KAEf,CAGA,sBAAOxkB,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOq2B,GAAQpb,oBAAoBtF,KAAM8D,GAC/C,GAAsB,iBAAXA,EAAX,CAGA,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IAJL,CAKF,GACF,EAOF3H,GAAmBukB,IAcnB,MACM2C,GAAiB,kBACjBC,GAAmB,gBACnBC,GAAY,IACb7C,GAAQhd,QACX0Z,QAAS,GACTp1B,OAAQ,CAAC,EAAG,GACZtJ,UAAW,QACX8+B,SAAU,8IACV5b,QAAS,SAEL4hB,GAAgB,IACjB9C,GAAQ/c,YACXyZ,QAAS,kCAOX,MAAMqG,WAAgB/C,GAEpB,kBAAWhd,GACT,OAAO6f,EACT,CACA,sBAAW5f,GACT,OAAO6f,EACT,CACA,eAAWjnB,GACT,MA7BW,SA8Bb,CAGA,cAAAqlB,GACE,OAAO5hB,KAAKgiB,aAAehiB,KAAK0jB,aAClC,CAGA,sBAAAxB,GACE,MAAO,CACL,CAACmB,IAAiBrjB,KAAKgiB,YACvB,CAACsB,IAAmBtjB,KAAK0jB,cAE7B,CACA,WAAAA,GACE,OAAO1jB,KAAK8d,yBAAyB9d,KAAK6E,QAAQuY,QACpD,CAGA,sBAAO3gB,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOo5B,GAAQne,oBAAoBtF,KAAM8D,GAC/C,GAAsB,iBAAXA,EAAX,CAGA,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IAJL,CAKF,GACF,EAOF3H,GAAmBsnB,IAcnB,MAEME,GAAc,gBAEdC,GAAiB,WAAWD,KAC5BE,GAAc,QAAQF,KACtBG,GAAwB,OAAOH,cAE/BI,GAAsB,SAEtBC,GAAwB,SAExBC,GAAqB,YAGrBC,GAAsB,GAAGD,mBAA+CA,uBAGxEE,GAAY,CAChBn8B,OAAQ,KAERo8B,WAAY,eACZC,cAAc,EACd93B,OAAQ,KACR+3B,UAAW,CAAC,GAAK,GAAK,IAElBC,GAAgB,CACpBv8B,OAAQ,gBAERo8B,WAAY,SACZC,aAAc,UACd93B,OAAQ,UACR+3B,UAAW,SAOb,MAAME,WAAkB9f,GACtB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GAGf9D,KAAKykB,aAAe,IAAIvzB,IACxB8O,KAAK0kB,oBAAsB,IAAIxzB,IAC/B8O,KAAK2kB,aAA6D,YAA9C1/B,iBAAiB+a,KAAK4E,UAAU5Y,UAA0B,KAAOgU,KAAK4E,SAC1F5E,KAAK4kB,cAAgB,KACrB5kB,KAAK6kB,UAAY,KACjB7kB,KAAK8kB,oBAAsB,CACzBC,gBAAiB,EACjBC,gBAAiB,GAEnBhlB,KAAKilB,SACP,CAGA,kBAAWvhB,GACT,OAAOygB,EACT,CACA,sBAAWxgB,GACT,OAAO4gB,EACT,CACA,eAAWhoB,GACT,MAhEW,WAiEb,CAGA,OAAA0oB,GACEjlB,KAAKklB,mCACLllB,KAAKmlB,2BACDnlB,KAAK6kB,UACP7kB,KAAK6kB,UAAUO,aAEfplB,KAAK6kB,UAAY7kB,KAAKqlB,kBAExB,IAAK,MAAMC,KAAWtlB,KAAK0kB,oBAAoBvlB,SAC7Ca,KAAK6kB,UAAUU,QAAQD,EAE3B,CACA,OAAAvgB,GACE/E,KAAK6kB,UAAUO,aACfzgB,MAAMI,SACR,CAGA,iBAAAf,CAAkBF,GAShB,OAPAA,EAAOvX,OAASmO,GAAWoJ,EAAOvX,SAAWlH,SAAS6G,KAGtD4X,EAAOsgB,WAAatgB,EAAO9b,OAAS,GAAG8b,EAAO9b,oBAAsB8b,EAAOsgB,WAC3C,iBAArBtgB,EAAOwgB,YAChBxgB,EAAOwgB,UAAYxgB,EAAOwgB,UAAUpiC,MAAM,KAAKY,KAAInF,GAAS4f,OAAOC,WAAW7f,MAEzEmmB,CACT,CACA,wBAAAqhB,GACOnlB,KAAK6E,QAAQwf,eAKlB9jB,GAAaC,IAAIR,KAAK6E,QAAQtY,OAAQs3B,IACtCtjB,GAAac,GAAGrB,KAAK6E,QAAQtY,OAAQs3B,GAAaG,IAAuB5kB,IACvE,MAAMomB,EAAoBxlB,KAAK0kB,oBAAoBvnC,IAAIiiB,EAAM7S,OAAOtB,MACpE,GAAIu6B,EAAmB,CACrBpmB,EAAMkD,iBACN,MAAM3G,EAAOqE,KAAK2kB,cAAgB/kC,OAC5BmE,EAASyhC,EAAkBnhC,UAAY2b,KAAK4E,SAASvgB,UAC3D,GAAIsX,EAAK8pB,SAKP,YAJA9pB,EAAK8pB,SAAS,CACZ9jC,IAAKoC,EACL2hC,SAAU,WAMd/pB,EAAKlQ,UAAY1H,CACnB,KAEJ,CACA,eAAAshC,GACE,MAAM5jC,EAAU,CACdka,KAAMqE,KAAK2kB,aACXL,UAAWtkB,KAAK6E,QAAQyf,UACxBF,WAAYpkB,KAAK6E,QAAQuf,YAE3B,OAAO,IAAIuB,sBAAqBxkB,GAAWnB,KAAK4lB,kBAAkBzkB,IAAU1f,EAC9E,CAGA,iBAAAmkC,CAAkBzkB,GAChB,MAAM0kB,EAAgBlI,GAAS3d,KAAKykB,aAAatnC,IAAI,IAAIwgC,EAAMpxB,OAAO4N,MAChEub,EAAWiI,IACf3d,KAAK8kB,oBAAoBC,gBAAkBpH,EAAMpxB,OAAOlI,UACxD2b,KAAK8lB,SAASD,EAAclI,GAAO,EAE/BqH,GAAmBhlB,KAAK2kB,cAAgBt/B,SAASC,iBAAiBmG,UAClEs6B,EAAkBf,GAAmBhlB,KAAK8kB,oBAAoBE,gBACpEhlB,KAAK8kB,oBAAoBE,gBAAkBA,EAC3C,IAAK,MAAMrH,KAASxc,EAAS,CAC3B,IAAKwc,EAAMqI,eAAgB,CACzBhmB,KAAK4kB,cAAgB,KACrB5kB,KAAKimB,kBAAkBJ,EAAclI,IACrC,QACF,CACA,MAAMuI,EAA2BvI,EAAMpxB,OAAOlI,WAAa2b,KAAK8kB,oBAAoBC,gBAEpF,GAAIgB,GAAmBG,GAGrB,GAFAxQ,EAASiI,IAEJqH,EACH,YAMCe,GAAoBG,GACvBxQ,EAASiI,EAEb,CACF,CACA,gCAAAuH,GACEllB,KAAKykB,aAAe,IAAIvzB,IACxB8O,KAAK0kB,oBAAsB,IAAIxzB,IAC/B,MAAMi1B,EAActgB,GAAe1T,KAAK6xB,GAAuBhkB,KAAK6E,QAAQtY,QAC5E,IAAK,MAAM65B,KAAUD,EAAa,CAEhC,IAAKC,EAAOn7B,MAAQiQ,GAAWkrB,GAC7B,SAEF,MAAMZ,EAAoB3f,GAAeC,QAAQugB,UAAUD,EAAOn7B,MAAO+U,KAAK4E,UAG1EjK,GAAU6qB,KACZxlB,KAAKykB,aAAa1yB,IAAIs0B,UAAUD,EAAOn7B,MAAOm7B,GAC9CpmB,KAAK0kB,oBAAoB3yB,IAAIq0B,EAAOn7B,KAAMu6B,GAE9C,CACF,CACA,QAAAM,CAASv5B,GACHyT,KAAK4kB,gBAAkBr4B,IAG3ByT,KAAKimB,kBAAkBjmB,KAAK6E,QAAQtY,QACpCyT,KAAK4kB,cAAgBr4B,EACrBA,EAAO8O,UAAU5E,IAAIstB,IACrB/jB,KAAKsmB,iBAAiB/5B,GACtBgU,GAAaqB,QAAQ5B,KAAK4E,SAAUgf,GAAgB,CAClD9jB,cAAevT,IAEnB,CACA,gBAAA+5B,CAAiB/5B,GAEf,GAAIA,EAAO8O,UAAU7W,SA9LQ,iBA+L3BqhB,GAAeC,QArLc,mBAqLsBvZ,EAAOyO,QAtLtC,cAsLkEK,UAAU5E,IAAIstB,SAGtG,IAAK,MAAMwC,KAAa1gB,GAAeI,QAAQ1Z,EA9LnB,qBAiM1B,IAAK,MAAMxJ,KAAQ8iB,GAAeM,KAAKogB,EAAWrC,IAChDnhC,EAAKsY,UAAU5E,IAAIstB,GAGzB,CACA,iBAAAkC,CAAkBxhC,GAChBA,EAAO4W,UAAU1B,OAAOoqB,IACxB,MAAMyC,EAAc3gB,GAAe1T,KAAK,GAAG6xB,MAAyBD,KAAuBt/B,GAC3F,IAAK,MAAM9E,KAAQ6mC,EACjB7mC,EAAK0b,UAAU1B,OAAOoqB,GAE1B,CAGA,sBAAOtnB,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAOm6B,GAAUlf,oBAAoBtF,KAAM8D,GACjD,GAAsB,iBAAXA,EAAX,CAGA,QAAqB/K,IAAjB1O,EAAKyZ,IAAyBA,EAAOrC,WAAW,MAAmB,gBAAXqC,EAC1D,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IAJL,CAKF,GACF,EAOFvD,GAAac,GAAGzhB,OAAQkkC,IAAuB,KAC7C,IAAK,MAAM2C,KAAO5gB,GAAe1T,KApOT,0BAqOtBqyB,GAAUlf,oBAAoBmhB,EAChC,IAOFtqB,GAAmBqoB,IAcnB,MAEMkC,GAAc,UACdC,GAAe,OAAOD,KACtBE,GAAiB,SAASF,KAC1BG,GAAe,OAAOH,KACtBI,GAAgB,QAAQJ,KACxBK,GAAuB,QAAQL,KAC/BM,GAAgB,UAAUN,KAC1BO,GAAsB,OAAOP,KAC7BQ,GAAiB,YACjBC,GAAkB,aAClBC,GAAe,UACfC,GAAiB,YACjBC,GAAW,OACXC,GAAU,MACVC,GAAoB,SACpBC,GAAoB,OACpBC,GAAoB,OAEpBC,GAA2B,mBAE3BC,GAA+B,QAAQD,MAIvCE,GAAuB,2EACvBC,GAAsB,YAFOF,uBAAiDA,mBAA6CA,OAE/EC,KAC5CE,GAA8B,IAAIP,8BAA6CA,+BAA8CA,4BAMnI,MAAMQ,WAAYtjB,GAChB,WAAAP,CAAY5kB,GACVolB,MAAMplB,GACNygB,KAAKoS,QAAUpS,KAAK4E,SAAS5J,QAdN,uCAelBgF,KAAKoS,UAOVpS,KAAKioB,sBAAsBjoB,KAAKoS,QAASpS,KAAKkoB,gBAC9C3nB,GAAac,GAAGrB,KAAK4E,SAAUoiB,IAAe5nB,GAASY,KAAK6M,SAASzN,KACvE,CAGA,eAAW7C,GACT,MAnDW,KAoDb,CAGA,IAAAsT,GAEE,MAAMsY,EAAYnoB,KAAK4E,SACvB,GAAI5E,KAAKooB,cAAcD,GACrB,OAIF,MAAME,EAASroB,KAAKsoB,iBACdC,EAAYF,EAAS9nB,GAAaqB,QAAQymB,EAAQ1B,GAAc,CACpE7mB,cAAeqoB,IACZ,KACa5nB,GAAaqB,QAAQumB,EAAWtB,GAAc,CAC9D/mB,cAAeuoB,IAEHrmB,kBAAoBumB,GAAaA,EAAUvmB,mBAGzDhC,KAAKwoB,YAAYH,EAAQF,GACzBnoB,KAAKyoB,UAAUN,EAAWE,GAC5B,CAGA,SAAAI,CAAUlpC,EAASmpC,GACZnpC,IAGLA,EAAQ8b,UAAU5E,IAAI+wB,IACtBxnB,KAAKyoB,UAAU5iB,GAAec,uBAAuBpnB,IAcrDygB,KAAKmF,gBAZY,KACsB,QAAjC5lB,EAAQic,aAAa,SAIzBjc,EAAQ4B,gBAAgB,YACxB5B,EAAQ6B,aAAa,iBAAiB,GACtC4e,KAAK2oB,gBAAgBppC,GAAS,GAC9BghB,GAAaqB,QAAQriB,EAASunC,GAAe,CAC3ChnB,cAAe4oB,KAPfnpC,EAAQ8b,UAAU5E,IAAIixB,GAQtB,GAE0BnoC,EAASA,EAAQ8b,UAAU7W,SAASijC,KACpE,CACA,WAAAe,CAAYjpC,EAASmpC,GACdnpC,IAGLA,EAAQ8b,UAAU1B,OAAO6tB,IACzBjoC,EAAQq7B,OACR5a,KAAKwoB,YAAY3iB,GAAec,uBAAuBpnB,IAcvDygB,KAAKmF,gBAZY,KACsB,QAAjC5lB,EAAQic,aAAa,SAIzBjc,EAAQ6B,aAAa,iBAAiB,GACtC7B,EAAQ6B,aAAa,WAAY,MACjC4e,KAAK2oB,gBAAgBppC,GAAS,GAC9BghB,GAAaqB,QAAQriB,EAASqnC,GAAgB,CAC5C9mB,cAAe4oB,KAPfnpC,EAAQ8b,UAAU1B,OAAO+tB,GAQzB,GAE0BnoC,EAASA,EAAQ8b,UAAU7W,SAASijC,KACpE,CACA,QAAA5a,CAASzN,GACP,IAAK,CAAC8nB,GAAgBC,GAAiBC,GAAcC,GAAgBC,GAAUC,IAASnmB,SAAShC,EAAMtiB,KACrG,OAEFsiB,EAAM0U,kBACN1U,EAAMkD,iBACN,MAAMyD,EAAW/F,KAAKkoB,eAAe/hC,QAAO5G,IAAY2b,GAAW3b,KACnE,IAAIqpC,EACJ,GAAI,CAACtB,GAAUC,IAASnmB,SAAShC,EAAMtiB,KACrC8rC,EAAoB7iB,EAAS3G,EAAMtiB,MAAQwqC,GAAW,EAAIvhB,EAASrV,OAAS,OACvE,CACL,MAAM8c,EAAS,CAAC2Z,GAAiBE,IAAgBjmB,SAAShC,EAAMtiB,KAChE8rC,EAAoB9qB,GAAqBiI,EAAU3G,EAAM7S,OAAQihB,GAAQ,EAC3E,CACIob,IACFA,EAAkBnW,MAAM,CACtBoW,eAAe,IAEjBb,GAAI1iB,oBAAoBsjB,GAAmB/Y,OAE/C,CACA,YAAAqY,GAEE,OAAOriB,GAAe1T,KAAK21B,GAAqB9nB,KAAKoS,QACvD,CACA,cAAAkW,GACE,OAAOtoB,KAAKkoB,eAAe/1B,MAAKzN,GAASsb,KAAKooB,cAAc1jC,MAAW,IACzE,CACA,qBAAAujC,CAAsBxjC,EAAQshB,GAC5B/F,KAAK8oB,yBAAyBrkC,EAAQ,OAAQ,WAC9C,IAAK,MAAMC,KAASqhB,EAClB/F,KAAK+oB,6BAA6BrkC,EAEtC,CACA,4BAAAqkC,CAA6BrkC,GAC3BA,EAAQsb,KAAKgpB,iBAAiBtkC,GAC9B,MAAMukC,EAAWjpB,KAAKooB,cAAc1jC,GAC9BwkC,EAAYlpB,KAAKmpB,iBAAiBzkC,GACxCA,EAAMtD,aAAa,gBAAiB6nC,GAChCC,IAAcxkC,GAChBsb,KAAK8oB,yBAAyBI,EAAW,OAAQ,gBAE9CD,GACHvkC,EAAMtD,aAAa,WAAY,MAEjC4e,KAAK8oB,yBAAyBpkC,EAAO,OAAQ,OAG7Csb,KAAKopB,mCAAmC1kC,EAC1C,CACA,kCAAA0kC,CAAmC1kC,GACjC,MAAM6H,EAASsZ,GAAec,uBAAuBjiB,GAChD6H,IAGLyT,KAAK8oB,yBAAyBv8B,EAAQ,OAAQ,YAC1C7H,EAAMyV,IACR6F,KAAK8oB,yBAAyBv8B,EAAQ,kBAAmB,GAAG7H,EAAMyV,MAEtE,CACA,eAAAwuB,CAAgBppC,EAAS8pC,GACvB,MAAMH,EAAYlpB,KAAKmpB,iBAAiB5pC,GACxC,IAAK2pC,EAAU7tB,UAAU7W,SApKN,YAqKjB,OAEF,MAAMmjB,EAAS,CAAC5N,EAAUoa,KACxB,MAAM50B,EAAUsmB,GAAeC,QAAQ/L,EAAUmvB,GAC7C3pC,GACFA,EAAQ8b,UAAUsM,OAAOwM,EAAWkV,EACtC,EAEF1hB,EAAOggB,GAA0BH,IACjC7f,EA5K2B,iBA4KI+f,IAC/BwB,EAAU9nC,aAAa,gBAAiBioC,EAC1C,CACA,wBAAAP,CAAyBvpC,EAASwC,EAAWpE,GACtC4B,EAAQgc,aAAaxZ,IACxBxC,EAAQ6B,aAAaW,EAAWpE,EAEpC,CACA,aAAAyqC,CAAc9Y,GACZ,OAAOA,EAAKjU,UAAU7W,SAASgjC,GACjC,CAGA,gBAAAwB,CAAiB1Z,GACf,OAAOA,EAAKtJ,QAAQ8hB,IAAuBxY,EAAOzJ,GAAeC,QAAQgiB,GAAqBxY,EAChG,CAGA,gBAAA6Z,CAAiB7Z,GACf,OAAOA,EAAKtU,QA5LO,gCA4LoBsU,CACzC,CAGA,sBAAO7S,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAO29B,GAAI1iB,oBAAoBtF,MACrC,GAAsB,iBAAX8D,EAAX,CAGA,QAAqB/K,IAAjB1O,EAAKyZ,IAAyBA,EAAOrC,WAAW,MAAmB,gBAAXqC,EAC1D,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,IAJL,CAKF,GACF,EAOFvD,GAAac,GAAGhc,SAAU0hC,GAAsBc,IAAsB,SAAUzoB,GAC1E,CAAC,IAAK,QAAQgC,SAASpB,KAAKiH,UAC9B7H,EAAMkD,iBAEJpH,GAAW8E,OAGfgoB,GAAI1iB,oBAAoBtF,MAAM6P,MAChC,IAKAtP,GAAac,GAAGzhB,OAAQqnC,IAAqB,KAC3C,IAAK,MAAM1nC,KAAWsmB,GAAe1T,KAAK41B,IACxCC,GAAI1iB,oBAAoB/lB,EAC1B,IAMF4c,GAAmB6rB,IAcnB,MAEMhjB,GAAY,YACZskB,GAAkB,YAAYtkB,KAC9BukB,GAAiB,WAAWvkB,KAC5BwkB,GAAgB,UAAUxkB,KAC1BykB,GAAiB,WAAWzkB,KAC5B0kB,GAAa,OAAO1kB,KACpB2kB,GAAe,SAAS3kB,KACxB4kB,GAAa,OAAO5kB,KACpB6kB,GAAc,QAAQ7kB,KAEtB8kB,GAAkB,OAClBC,GAAkB,OAClBC,GAAqB,UACrBrmB,GAAc,CAClByc,UAAW,UACX6J,SAAU,UACV1J,MAAO,UAEH7c,GAAU,CACd0c,WAAW,EACX6J,UAAU,EACV1J,MAAO,KAOT,MAAM2J,WAAcxlB,GAClB,WAAAP,CAAY5kB,EAASukB,GACnBa,MAAMplB,EAASukB,GACf9D,KAAK4gB,SAAW,KAChB5gB,KAAKmqB,sBAAuB,EAC5BnqB,KAAKoqB,yBAA0B,EAC/BpqB,KAAKkhB,eACP,CAGA,kBAAWxd,GACT,OAAOA,EACT,CACA,sBAAWC,GACT,OAAOA,EACT,CACA,eAAWpH,GACT,MA/CS,OAgDX,CAGA,IAAAsT,GACoBtP,GAAaqB,QAAQ5B,KAAK4E,SAAUglB,IACxC5nB,mBAGdhC,KAAKqqB,gBACDrqB,KAAK6E,QAAQub,WACfpgB,KAAK4E,SAASvJ,UAAU5E,IA/CN,QAsDpBuJ,KAAK4E,SAASvJ,UAAU1B,OAAOmwB,IAC/BjuB,GAAOmE,KAAK4E,UACZ5E,KAAK4E,SAASvJ,UAAU5E,IAAIszB,GAAiBC,IAC7ChqB,KAAKmF,gBARY,KACfnF,KAAK4E,SAASvJ,UAAU1B,OAAOqwB,IAC/BzpB,GAAaqB,QAAQ5B,KAAK4E,SAAUilB,IACpC7pB,KAAKsqB,oBAAoB,GAKGtqB,KAAK4E,SAAU5E,KAAK6E,QAAQub,WAC5D,CACA,IAAAxQ,GACO5P,KAAKuqB,YAGQhqB,GAAaqB,QAAQ5B,KAAK4E,SAAU8kB,IACxC1nB,mBAQdhC,KAAK4E,SAASvJ,UAAU5E,IAAIuzB,IAC5BhqB,KAAKmF,gBANY,KACfnF,KAAK4E,SAASvJ,UAAU5E,IAAIqzB,IAC5B9pB,KAAK4E,SAASvJ,UAAU1B,OAAOqwB,GAAoBD,IACnDxpB,GAAaqB,QAAQ5B,KAAK4E,SAAU+kB,GAAa,GAGrB3pB,KAAK4E,SAAU5E,KAAK6E,QAAQub,YAC5D,CACA,OAAArb,GACE/E,KAAKqqB,gBACDrqB,KAAKuqB,WACPvqB,KAAK4E,SAASvJ,UAAU1B,OAAOowB,IAEjCplB,MAAMI,SACR,CACA,OAAAwlB,GACE,OAAOvqB,KAAK4E,SAASvJ,UAAU7W,SAASulC,GAC1C,CAIA,kBAAAO,GACOtqB,KAAK6E,QAAQolB,WAGdjqB,KAAKmqB,sBAAwBnqB,KAAKoqB,0BAGtCpqB,KAAK4gB,SAAW/iB,YAAW,KACzBmC,KAAK4P,MAAM,GACV5P,KAAK6E,QAAQ0b,QAClB,CACA,cAAAiK,CAAeprB,EAAOqrB,GACpB,OAAQrrB,EAAMqB,MACZ,IAAK,YACL,IAAK,WAEDT,KAAKmqB,qBAAuBM,EAC5B,MAEJ,IAAK,UACL,IAAK,WAEDzqB,KAAKoqB,wBAA0BK,EAIrC,GAAIA,EAEF,YADAzqB,KAAKqqB,gBAGP,MAAM5c,EAAcrO,EAAMU,cACtBE,KAAK4E,WAAa6I,GAAezN,KAAK4E,SAASpgB,SAASipB,IAG5DzN,KAAKsqB,oBACP,CACA,aAAApJ,GACE3gB,GAAac,GAAGrB,KAAK4E,SAAU0kB,IAAiBlqB,GAASY,KAAKwqB,eAAeprB,GAAO,KACpFmB,GAAac,GAAGrB,KAAK4E,SAAU2kB,IAAgBnqB,GAASY,KAAKwqB,eAAeprB,GAAO,KACnFmB,GAAac,GAAGrB,KAAK4E,SAAU4kB,IAAepqB,GAASY,KAAKwqB,eAAeprB,GAAO,KAClFmB,GAAac,GAAGrB,KAAK4E,SAAU6kB,IAAgBrqB,GAASY,KAAKwqB,eAAeprB,GAAO,IACrF,CACA,aAAAirB,GACEnd,aAAalN,KAAK4gB,UAClB5gB,KAAK4gB,SAAW,IAClB,CAGA,sBAAOnkB,CAAgBqH,GACrB,OAAO9D,KAAKwH,MAAK,WACf,MAAMnd,EAAO6/B,GAAM5kB,oBAAoBtF,KAAM8D,GAC7C,GAAsB,iBAAXA,EAAqB,CAC9B,QAA4B,IAAjBzZ,EAAKyZ,GACd,MAAM,IAAIU,UAAU,oBAAoBV,MAE1CzZ,EAAKyZ,GAAQ9D,KACf,CACF,GACF,ECr0IK,SAAS0qB,GAAcruB,GACD,WAAvBhX,SAASuX,WAAyBP,IACjChX,SAASyF,iBAAiB,mBAAoBuR,EACrD,CDy0IAwK,GAAqBqjB,IAMrB/tB,GAAmB+tB,IEpyInBQ,IAzCA,WAC2B,GAAGt4B,MAAM5U,KAChC6H,SAAS+a,iBAAiB,+BAETtd,KAAI,SAAU6nC,GAC/B,OAAO,IAAI,GAAkBA,EAAkB,CAC7CpK,MAAO,CAAE1Q,KAAM,IAAKD,KAAM,MAE9B,GACF,IAiCA8a,IA5BA,WACYrlC,SAASm9B,eAAe,mBAC9B13B,iBAAiB,SAAS,WAC5BzF,SAAS6G,KAAKT,UAAY,EAC1BpG,SAASC,gBAAgBmG,UAAY,CACvC,GACF,IAuBAi/B,IArBA,WACE,IAAIE,EAAMvlC,SAASm9B,eAAe,mBAC9BqI,EAASxlC,SACVylC,uBAAuB,aAAa,GACpCxnC,wBACH1D,OAAOkL,iBAAiB,UAAU,WAC5BkV,KAAK+qB,UAAY/qB,KAAKgrB,SAAWhrB,KAAKgrB,QAAUH,EAAOjtC,OACzDgtC,EAAI7pC,MAAMgxB,QAAU,QAEpB6Y,EAAI7pC,MAAMgxB,QAAU,OAEtB/R,KAAK+qB,UAAY/qB,KAAKgrB,OACxB,GACF,IAUAprC,OAAOqrC,UAAY","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.3';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n }\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both