Modules in the context of High-Performance Computing (HPC) systems
are packages that I can load and unload dynamically to configure my
environment(s) with the necessary software. Here’s a brief explanation
of each module listed:
Compiler Modules
- amd/aocc/4.1.0
- AMD AOCC (AMD Optimizing C/C++ Compiler): This is a
high-performance, production-quality code generation tool. It supports
C, C++, and Fortran programming languages and is specifically optimized
for AMD processors.
- gcc/11.2.0 and gcc/13.2.0
- GCC (GNU Compiler Collection): These are
widely-used compilers for C, C++, and Fortran. GCC 11.2.0 and GCC 13.2.0
are different versions, with the latter being the default (denoted by
‘D’) version in this environment. GCC is known for its portability and
is a standard compiler on many Linux distributions.
- intel/oneapi/2023.2
- Intel OneAPI: This is a comprehensive,
cross-architecture programming toolkit. It includes compilers (like ICC
for C/C++ and IFORT for Fortran), libraries, and analysis tools
optimized for Intel processors. OneAPI is designed to help developers
build high-performance applications.
- nvidia/nvhpc/23.7
- NVIDIA HPC SDK: This includes compilers, libraries,
and tools to develop applications for NVIDIA GPUs and CPUs. The NVHPC
(formerly PGI) compiler suite supports C, C++, and Fortran programming
languages and is optimized for CUDA and GPU-accelerated
applications.
Application Modules
- amber/22
- AMBER (Assisted Model Building with Energy
Refinement): A suite of biomolecular simulation programs. It is
used primarily for molecular dynamics simulations of proteins and
nucleic acids.
- apptainer/1.1.9
- Apptainer: Formerly known as Singularity, it is a
container platform designed for HPC environments. It allows users to
create and run containers that package up applications and their
dependencies in a portable manner.
- conda
- Conda: An open-source package management and
environment management system. It allows you to create isolated
environments and install packages from the Conda repository. It is
particularly popular for managing Python environments and packages.
- gaussian/g16c02
- Gaussian: Software used for computational
chemistry. Gaussian 16 is a version of the Gaussian software suite,
which performs electronic structure calculations. It is widely used in
chemistry for predicting the energies, molecular structures, and
vibrational frequencies of molecular systems.
- julia/1.9.2
- Julia: A high-level, high-performance programming
language for technical computing. It is designed for numerical and
scientific computing, and version 1.9.2 is a specific release of this
language.
- lammps/may22
- LAMMPS (Large-scale Atomic/Molecular Massively Parallel
Simulator): A classical molecular dynamics code that can model
an ensemble of particles in a liquid, solid, or gaseous state. LAMMPS is
widely used in materials science, chemistry, and physics.
- spack
- Spack: A flexible package manager that supports
multiple versions and configurations of software. It is particularly
designed for HPC environments and allows users to easily install,
manage, and swap software packages.
To use the NVIDIA NVHPC 23.7 compiler suite on HPC system:
Step 1: Load the NVHPC Module
Load the NVIDIA NVHPC module -sets up environment with the necessary
paths and environment variables.
module load nvidia/nvhpc/23.7
Verify that the module has been loaded correctly by checking the
currently loaded modules:
module list
Step 2: Compile my Code
Once loaded,use the NVHPC compilers to compile code. NVHPC includes
several compilers:
nvc
for C
nvc++
for C++
nvfortran
for Fortran
Here are examples of how to compile C, C++, and Fortran code:
C Code Compilation
nvc -o my_program my_program.c
C++ Code Compilation
nvc++ -o my_program my_program.cpp
Fortran Code Compilation
nvfortran -o my_program my_program.f90
Step 3: Run my Program
After compiling code run the executable as I would any other
program:
./my_program
Step 4: Using CUDA
If code utilizes CUDA for GPU acceleration, NVHPC supports CUDA
directly. Here is an example of compiling a CUDA program:
nvc++ -o my_cuda_program my_cuda_program.cu
Example Workflow
Example workflow:
Load the NVHPC module:
module load nvidia/nvhpc/23.7
Compile C program:
nvc -o hello_world hello_world.c
Run compiled program:
./hello_world
Example CUDA Program Compilation
Here’s a brief example of compiling a CUDA program:
hello_world.cu:
#include <stdio.h>
__global__ void helloFromGPU() {
printf("Hello World from GPU!\n");
}
int main() {
helloFromGPU<<<1, 1>>>();
cudaDeviceSynchronize();
return 0;
}
Compiling and Running:
Compile:
nvc++ -o hello_world hello_world.cu
Run:
./hello_world
Creating a Quantum Generative Adversarial Network (QGAN) for anomaly
detection in Geographic Information Systems (GIS) is an advanced task
that involves integrating classical and quantum computing resources.
Here’s a detailed guide on how to set up the environment and the tools I
could use:
Step 2: Set Up the Classical Environment
First, ensure that I have Python and necessary packages installed.
Using conda
to create an isolated environment is
recommended.
1. Install Anaconda or Miniconda
Download and install Anaconda or Miniconda from the official
website.
2. Create a Conda Environment
conda create -n qgan_env python=3.9
conda activate qgan_env
3. Install Required Packages
conda install numpy pandas geopandas gdal matplotlib
pip install qiskit tensorflow torch torchvision
Step 3: Set Up Quantum Computing SDK
1. Install Qiskit
If using IBM’s Qiskit:
pip install qiskit
For Cirq (Google):
pip install cirq
Step 4: Integrate Classical and Quantum Components
Here’s an example of a workflow integrating classical and quantum
components for a QGAN:
1. Classical GAN for GIS Data
Create a classical GAN for generating GIS data anomalies. Below is a
simplified example using PyTorch:
import torch
import torch.nn as nn
import torch.optim as optim
import geopandas as gpd
# Example: Simple GAN components
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.main = nn.Sequential(
nn.Linear(100, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, 1024),
nn.Tanh()
)
def forward(self, input):
return self.main(input)
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.main = nn.Sequential(
nn.Linear(1024, 512),
nn.LeakyReLU(0.2),
nn.Linear(512, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, 1),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
# Initialize models
G = Generator()
D = Discriminator()
# Optimizers and loss function
optimizerG = optim.Adam(G.parameters(), lr=0.0002)
optimizerD = optim.Adam(D.parameters(), lr=0.0002)
criterion = nn.BCELoss()
# Training loop placeholder
for epoch in range(epochs):
# Training code here
pass
2. Integrate Quantum Component
Using Qiskit for quantum parts of QGAN:
from qiskit import QuantumCircuit, transpile, Aer, execute
from qiskit.visualization import plot_histogram
# Quantum generator
def quantum_generator():
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0)
return qc
# Simulate the quantum circuit
backend = Aer.get_backend('qasm_simulator')
qc = quantum_generator()
job = execute(qc, backend, shots=1000)
result = job.result()
counts = result.get_counts(qc)
print(counts)
plot_histogram(counts)
Step 5: Integrate Classical and Quantum Components in the QGAN
Combine classical and quantum components. Here’s a simplified
pseudocode integration:
# Example of integrating quantum-generated data with classical GAN training
for epoch in range(epochs):
for i, data in enumerate(dataloader):
# Quantum data generation
qc = quantum_generator()
job = execute(qc, backend, shots=1)
result = job.result()
quantum_data = list(result.get_counts(qc).keys())[0]
# Convert quantum data to input format
quantum_input = torch.Tensor([float(bit) for bit in quantum_data])
# Classical GAN training steps
real_data = data[0]
fake_data = G(quantum_input)
# Update Discriminator
D.zero_grad()
real_output = D(real_data)
fake_output = D(fake_data.detach())
lossD = criterion(real_output, torch.ones_like(real_output)) + criterion(fake_output, torch.zeros_like(fake_output))
lossD.backward()
optimizerD.step()
# Update Generator
G.zero_grad()
fake_output = D(fake_data)
lossG = criterion(fake_output, torch.ones_like(fake_output))
lossG.backward()
optimizerG.step()
# Print losses (optional)
print(f'Epoch [{epoch}/{epochs}], Step [{i}/{len(dataloader)}], Loss D: {lossD.item()}, Loss G: {lossG.item()}')
Conclusion
- Set up the environment: Install necessary tools
using
conda
and pip
.
- Create classical and quantum models: Use PyTorch
for classical GAN and Qiskit for quantum components.
- Integrate models: Combine quantum-generated data
with classical GAN training.
M3
The modules and compilers available on the HPC system (m3) provide
various tools for scientific computing, development, and analysis.
Here’s an explanation of each module and compiler listed:
Applications Modules
- R
- Versions: 4.2.3, 4.3.0, 4.3.2, 4.4.0 (D)
- R: A programming language and software environment
used for statistical computing and graphics. It is widely used for data
analysis and visualization.
- ampl/20231031
- AMPL: A comprehensive and powerful algebraic
modeling language for linear and nonlinear optimization problems.
- charmm/c47b2
- CHARMM: A program for macromolecular dynamics and
mechanics simulations, used for studying the behavior of molecular
systems.
- dynare
- Versions: 5.4, 5.5 (D)
- Dynare: A software platform for handling a wide
class of economic models, particularly those involving dynamic
stochastic general equilibrium (DSGE) and overlapping generations (OLG)
models.
- gaussian
- Versions: g16c/haswell, g16c/zen3 (D)
- Gaussian: Software for computational chemistry used
for electronic structure modeling.
- julia
- Versions: 1.8.5, 1.9.1 (D)
- Julia: A high-level, high-performance programming
language for technical computing.
- nbo
- Versions: 7.0/i4, 7.0/i8 (D)
- NBO (Natural Bond Orbital): Software for studying
chemical bonding and electron density.
- snid/5.0
- SNID (SuperNova Identification): A tool for
classifying supernova spectra.
- vmd/1.9.3
- VMD (Visual Molecular Dynamics): Software for
molecular modeling and bioinformatics.
- ansys/electronics
- Versions: 23.1, 23.2 (D)
- ANSYS Electronics: Simulation software for
electronics, electromagnetic, and thermal analysis.
- comsol/6.1
- COMSOL: A multiphysics simulation software for
modeling and simulating engineering problems.
- conda
- Conda: An open-source package management and
environment management system that allows users to create isolated
environments and install packages.
- cplex/22.1.1
- CPLEX: Optimization software for solving linear
programming, mixed integer programming, and other types of optimization
problems.
- eog/3.28.4
- EOG (Eye of GNOME): An image viewer for the GNOME
desktop environment.
- gedit/3.28.1
- Gedit: A text editor for the GNOME desktop
environment.
- gaussview/6.0.16
- GaussView: A graphical interface for Gaussian
software, aiding in the setup and analysis of computational chemistry
calculations.
- mathematica
- Versions: 13.3.0, r2024a (D)
- Mathematica: A computational software used in
scientific, engineering, mathematical fields, and other areas for
computations and visualization.
- matlab
- Versions: r2023a, r2024a (D)
- MATLAB: A high-level programming language and
environment used for numerical computing, data analysis, and algorithm
development.
- orca/5.0.4
- ORCA: An electronic structure software package for
quantum chemistry calculations.
- q-chem
- Versions: 5.2.2, 6.0.2 (D)
- Q-Chem: A software package for computational
chemistry, focusing on quantum chemical calculations.
- stata
- Versions: mp-17, mp-18 (D)
- Stata: A software for statistics and data science,
used for data analysis, data management, and graphics.
- aimall/19.10.12
- AIMAll: Software for analyzing molecular wave
functions and electron density.
- apptainer/1.1.6
- Apptainer (formerly Singularity): A container
platform designed for HPC environments, allowing portable and
reproducible software environments.
- blender/4.0.2
- Blender: Open-source 3D modeling, animation, and
rendering software.
- cfour
- Versions: 2.1/mpi, 2.1/nompi (D)
- CFOUR: A quantum chemistry program package for
high-accuracy calculations.
- crystal
- Versions: 23/1.0.1, 23/1.0.1-1 (D)
- CRYSTAL: Software for computing the electronic
structure of periodic systems.
- demon/6.2.2
- deMon: A software for density functional theory
(DFT) calculations.
- ds9/8.4.1
- DS9: A tool for astronomical data visualization and
analysis.
- gamess/2022.2
- GAMESS (General Atomic and Molecular Electronic Structure
System): A program for computational chemistry.
- guori
- Versions: 10.0.1, 11.0.1 (D)
- Gurobi: An optimization solver for mathematical
programming.
- idl/8.0
- IDL (Interactive Data Language): A programming
language used for data analysis, visualization, and cross-platform
application development.
- molden/6.9
- Molden: A visualization package for molecular and
electronic structure.
- molpro
- Versions: 2022.3, 2024.1 (D)
- Molpro: Software for high-accuracy electronic
structure calculations.
- mojo/0.6.1
- Mojo: A framework for creating machine learning
pipelines.
- quantum_atk
- Versions: 2022.03-SP1, 2022.12-SP1 (D)
- QuantumATK: A software platform for atomic-scale
modeling and simulation.
- sas/9.4m7
- SAS: A software suite for advanced analytics,
business intelligence, data management, and predictive analytics.
- synopsys/photonicsolutions/2021.09-3
- Synopsys Photonic Solutions: Software tools for
designing and simulating photonic and optoelectronic components and
systems.
- tcad/2022
- TCAD: Technology Computer-Aided Design software
used for simulating semiconductor processing and devices.
- texlive/2023
- TeX Live: A comprehensive TeX system for editing
and publishing documents.
- vasp/5.4.4
- VASP (Vienna Ab initio Simulation Package): A
software for atomic-scale materials modeling.
Compiler Modules
- amd/4.0.0
- AMD Optimizing C/C++ Compiler: Optimized for AMD
processors, supporting C, C++, and Fortran programming languages.
- gcc
- Versions: 6.3.0, 11.2.0 (D)
- GCC (GNU Compiler Collection): A widely-used
compiler supporting C, C++, and Fortran.
- intel
- Versions: 2023.1, oneapi/2023.2
- Intel Compilers: Optimized for Intel processors,
supporting high-performance computing and applications.
- nvidia
- Versions: 21.3, 23.5 (D)
- NVIDIA HPC SDK: Compilers and tools for
GPU-accelerated applications, supporting CUDA and other parallel
programming models.
- rust/1.71.0
- Rust: A systems programming language focused on
safety, speed, and concurrency.
Usage Example
To use these modules on the HPC system, you need to load the required
modules before compiling or running my code. Here’s an example of how to
load a module and compile a program:
# Load the GCC compiler module
module load gcc/11.2.0
# Compile a C program
gcc -o my_program my_program.c
# Load the Python environment with Conda
module load conda
conda activate my_env
# Run a Python script
python my_script.py
project on m3
To develop a Quantum Generative Adversarial Network (QGAN) on the M3
HPC system for my project, you will need to integrate quantum computing
with classical computing tools. Here’s a comprehensive step-by-step
guide to help you get started:
Step 1: Set Up my Environment
1. Connect to the M3 HPC System
Use SSH to connect to the M3 HPC system.
ssh jmcphaul@m3login01
2. Load Necessary Modules
First, you need to load the necessary modules for my environment.
This includes compilers, Python environments, and any relevant
applications.
module load gcc/11.2.0
module load conda
module load nvidia/23.5
Step 2: Create and Activate a Conda Environment
Use Conda to create an isolated environment for my project, which
will include the necessary Python packages for both classical and
quantum computing.
conda create -n qgan_env python=3.9
conda activate qgan_env
Step 3: Install Required Python Packages
With my Conda environment activated, install the necessary Python
packages for quantum computing, machine learning, and GIS
applications.
# Install basic packages for GIS and quantum computing
conda install numpy pandas geopandas gdal matplotlib
pip install qiskit tensorflow torch torchvision
Step 4: Develop the QGAN
You will create the QGAN by integrating both classical and quantum
components. Below is an example setup.
Classical GAN for GIS Data (using PyTorch)
Create and train a classical GAN for generating and analyzing GIS
data.
GAN Components
import torch
import torch.nn as nn
import torch.optim as optim
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.main = nn.Sequential(
nn.Linear(100, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, 1024),
nn.Tanh()
)
def forward(self, input):
return self.main(input)
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.main = nn.Sequential(
nn.Linear(1024, 512),
nn.LeakyReLU(0.2),
nn.Linear(512, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, 1),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
# Initialize models
G = Generator()
D = Discriminator()
# Optimizers and loss function
optimizerG = optim.Adam(G.parameters(), lr=0.0002)
optimizerD = optim.Adam(D.parameters(), lr=0.0002)
criterion = nn.BCELoss()
# Dummy training loop placeholder
epochs = 100
for epoch in range(epochs):
# Training code here
pass
Integrate Quantum Component using Qiskit
Add the quantum component to my GAN using Qiskit for quantum data
generation.
from qiskit import QuantumCircuit, Aer, execute
from qiskit.visualization import plot_histogram
def quantum_generator():
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0)
return qc
backend = Aer.get_backend('qasm_simulator')
qc = quantum_generator()
job = execute(qc, backend, shots=1000)
result = job.result()
counts = result.get_counts(qc)
print(counts)
plot_histogram(counts)
Step 5: Integrate Classical and Quantum Components in the QGAN
Combine quantum-generated data with classical GAN training:
import geopandas as gpd
import numpy as np
# Example of integrating quantum-generated data with classical GAN training
for epoch in range(epochs):
for i, data in enumerate(dataloader):
# Quantum data generation
qc = quantum_generator()
job = execute(qc, backend, shots=1)
result = job.result()
quantum_data = list(result.get_counts(qc).keys())[0]
# Convert quantum data to input format
quantum_input = torch.Tensor([float(bit) for bit in quantum_data])
# Classical GAN training steps
real_data = data[0]
fake_data = G(quantum_input)
# Update Discriminator
D.zero_grad()
real_output = D(real_data)
fake_output = D(fake_data.detach())
lossD = criterion(real_output, torch.ones_like(real_output)) + criterion(fake_output, torch.zeros_like(fake_output))
lossD.backward()
optimizerD.step()
# Update Generator
G.zero_grad()
fake_output = D(fake_data)
lossG = criterion(fake_output, torch.ones_like(fake_output))
lossG.backward()
optimizerG.step()
# Print losses (optional)
print(f'Epoch [{epoch}/{epochs}], Step [{i}/{len(dataloader)}], Loss D: {lossD.item()}, Loss G: {lossG.item()}')
Step 6: Prepare GIS Data for Training
Load and preprocess GIS data for use in the QGAN.
# Load GIS data using GeoPandas
gis_data = gpd.read_file('path_to_my_gis_data.shp')
# Preprocess GIS data
# Example: normalize data, convert to tensor, etc.
gis_data = gis_data.to_crs(epsg=4326) # Convert to a common coordinate reference system
data_tensor = torch.tensor(gis_data.values, dtype=torch.float32)
Step 7: Train and Evaluate the QGAN
Run the training process and evaluate the model’s performance.
# Training and evaluation loop
for epoch in range(epochs):
for i, data in enumerate(dataloader):
# Quantum and classical data integration and training
# my training code here...
# Save the model
torch.save(G.state_dict(), 'generator_model.pth')
torch.save(D.state_dict(), 'discriminator_model.pth')
# Evaluate the model
# my evaluation code here...
Step 8: Run on the HPC System
Submitjob to the HPC system using a job scheduler like SLURM or PBS.
Here is an example SLURM script:
#!/bin/bash
#SBATCH --job-name=qgan_gis
#SBATCH --output=qgan_gis_output.txt
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --mem=16G
#SBATCH --time=24:00:00
#SBATCH --partition=gpu
#SBATCH --gpus=1
module load gcc/11.2.0
module load conda
module load nvidia/23.5
conda activate qgan_env
python my_qgan_script.py
Save this script as submit_job.sh
and submit it to the
job scheduler:
sbatch submit_job.sh
HPC
conda
name: essential_env channels: - defaults - conda-forge dependencies:
- python=3.10 - numpy - pandas - matplotlib - scipy - scikit-learn -
jupyter - pip - pip: - qiskit - pytorch - torchvision - tensorflow -
keras - plotly - seaborn
conda env create -f environment3.yml
pip uninstall ultralytics rustworkx numpy pip install ultralytics
rustworkx==0.14.2
conda env export > environment.yml
conda update –all –dry-run
conda create -n new_env_name python=3.10
conda activate new_env_name
conda install numpy scipy matplotlib seaborn ...
conda env export > environment_name.yml
dependencies:
- numpy=1.26.4
- scipy=1.14.0
- matplotlib=3.8.4
- seaborn=0.13.2
- ...
``{sh} conda env create -f environment_name.yml
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgYmFzaFxuY29uZGEgY29uZmlnIC0tYWRkIGNoYW5uZWxzIGNvbmRhLWZvcmdlXG5jb25kYSBjb25maWcgLS1zZXQgY2hhbm5lbF9wcmlvcml0eSBzdHJpY3RcbmBgYCJ9 -->
```bash
conda config --add channels conda-forge
conda config --set channel_priority strict
nano environment.yml - save as new
name: my_new_env
channels:
- conda-forge
- defaults
dependencies:
- python=3.10
- numpy=1.26.4
- scipy=1.14.0
- matplotlib=3.8.4
- seaborn=0.13.2
- pandas=2.2.2
- tqdm=4.66.4
- jupyter
- ipykernel
- scikit-learn=1.5.0
- pytorch=2.3.1
- torchvision=0.18.1
- qiskit=1.1.1
- pyunicorn
- tensorflow
- keras
- plotly
- h5py
- h5netcdf
- folium
- cartopy
- py-opencv
- opencv
###conda env export > environment_name.yml
module load gcc/11.2.0
module load conda
salloc --nodes=1 --ntasks=1 --cpus-per-task=4 --mem=16G --time=02:00:00 --partition=batch --gres=gpu:1
module load gcc/11.2.0
module load conda
conda activate qgan_env
Full Command Sequence
Allocate resources on the HPC system:
sh
Copy code
salloc --nodes=1 --ntasks=1 --cpus-per-task=4 --mem=16G --time=02:00:00 --partition=batch --gres=gpu:1
Load necessary modules and activate Conda environment:
sh
Copy code
module load gcc/11.2.0
module load conda
conda activate qgan_env
conda install jupyter
Start Jupyter Notebook:
sh
Copy code
jupyter-notebook --no-browser --port=8888
If this fails, try JupyterLab:
sh
Copy code
jupyter lab --no-browser --port=8888
Set up SSH tunneling on your local machine:
sh
Copy code
ssh -N -L 8888:localhost:8888 jmcphaul@slogin-02
Access Jupyter Notebook via your web browser:
ruby
Copy code
http://localhost:8888/?token=YOUR_TOKEN
LS0tDQp0aXRsZTogInN1cGVycG9kIG1vZHVsZXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpNb2R1bGVzIGluIHRoZSBjb250ZXh0IG9mIEhpZ2gtUGVyZm9ybWFuY2UgQ29tcHV0aW5nIChIUEMpIHN5c3RlbXMgYXJlDQpwYWNrYWdlcyB0aGF0IEkgY2FuIGxvYWQgYW5kIHVubG9hZCBkeW5hbWljYWxseSB0byBjb25maWd1cmUgbXkNCmVudmlyb25tZW50KHMpIHdpdGggdGhlIG5lY2Vzc2FyeSBzb2Z0d2FyZS4gSGVyZeKAmXMgYSBicmllZiBleHBsYW5hdGlvbg0Kb2YgZWFjaCBtb2R1bGUgbGlzdGVkOg0KDQojIyMgQ29tcGlsZXIgTW9kdWxlcw0KDQoxLiAgKiphbWQvYW9jYy80LjEuMCoqDQogICAgLSAgICoqQU1EIEFPQ0MgKEFNRCBPcHRpbWl6aW5nIEMvQysrIENvbXBpbGVyKSoqOiBUaGlzIGlzIGENCiAgICAgICAgaGlnaC1wZXJmb3JtYW5jZSwgcHJvZHVjdGlvbi1xdWFsaXR5IGNvZGUgZ2VuZXJhdGlvbiB0b29sLiBJdA0KICAgICAgICBzdXBwb3J0cyBDLCBDKyssIGFuZCBGb3J0cmFuIHByb2dyYW1taW5nIGxhbmd1YWdlcyBhbmQgaXMNCiAgICAgICAgc3BlY2lmaWNhbGx5IG9wdGltaXplZCBmb3IgQU1EIHByb2Nlc3NvcnMuDQoyLiAgKipnY2MvMTEuMi4wIGFuZCBnY2MvMTMuMi4wKioNCiAgICAtICAgKipHQ0MgKEdOVSBDb21waWxlciBDb2xsZWN0aW9uKSoqOiBUaGVzZSBhcmUgd2lkZWx5LXVzZWQNCiAgICAgICAgY29tcGlsZXJzIGZvciBDLCBDKyssIGFuZCBGb3J0cmFuLiBHQ0MgMTEuMi4wIGFuZCBHQ0MgMTMuMi4wIGFyZQ0KICAgICAgICBkaWZmZXJlbnQgdmVyc2lvbnMsIHdpdGggdGhlIGxhdHRlciBiZWluZyB0aGUgZGVmYXVsdCAoZGVub3RlZA0KICAgICAgICBieSAnRCcpIHZlcnNpb24gaW4gdGhpcyBlbnZpcm9ubWVudC4gR0NDIGlzIGtub3duIGZvciBpdHMNCiAgICAgICAgcG9ydGFiaWxpdHkgYW5kIGlzIGEgc3RhbmRhcmQgY29tcGlsZXIgb24gbWFueSBMaW51eA0KICAgICAgICBkaXN0cmlidXRpb25zLg0KMy4gICoqaW50ZWwvb25lYXBpLzIwMjMuMioqDQogICAgLSAgICoqSW50ZWwgT25lQVBJKio6IFRoaXMgaXMgYSBjb21wcmVoZW5zaXZlLCBjcm9zcy1hcmNoaXRlY3R1cmUNCiAgICAgICAgcHJvZ3JhbW1pbmcgdG9vbGtpdC4gSXQgaW5jbHVkZXMgY29tcGlsZXJzIChsaWtlIElDQyBmb3IgQy9DKysNCiAgICAgICAgYW5kIElGT1JUIGZvciBGb3J0cmFuKSwgbGlicmFyaWVzLCBhbmQgYW5hbHlzaXMgdG9vbHMgb3B0aW1pemVkDQogICAgICAgIGZvciBJbnRlbCBwcm9jZXNzb3JzLiBPbmVBUEkgaXMgZGVzaWduZWQgdG8gaGVscCBkZXZlbG9wZXJzDQogICAgICAgIGJ1aWxkIGhpZ2gtcGVyZm9ybWFuY2UgYXBwbGljYXRpb25zLg0KNC4gICoqbnZpZGlhL252aHBjLzIzLjcqKg0KICAgIC0gICAqKk5WSURJQSBIUEMgU0RLKio6IFRoaXMgaW5jbHVkZXMgY29tcGlsZXJzLCBsaWJyYXJpZXMsIGFuZA0KICAgICAgICB0b29scyB0byBkZXZlbG9wIGFwcGxpY2F0aW9ucyBmb3IgTlZJRElBIEdQVXMgYW5kIENQVXMuIFRoZQ0KICAgICAgICBOVkhQQyAoZm9ybWVybHkgUEdJKSBjb21waWxlciBzdWl0ZSBzdXBwb3J0cyBDLCBDKyssIGFuZCBGb3J0cmFuDQogICAgICAgIHByb2dyYW1taW5nIGxhbmd1YWdlcyBhbmQgaXMgb3B0aW1pemVkIGZvciBDVURBIGFuZA0KICAgICAgICBHUFUtYWNjZWxlcmF0ZWQgYXBwbGljYXRpb25zLg0KDQojIyMgQXBwbGljYXRpb24gTW9kdWxlcw0KDQoxLiAgKiphbWJlci8yMioqDQogICAgLSAgICoqQU1CRVIgKEFzc2lzdGVkIE1vZGVsIEJ1aWxkaW5nIHdpdGggRW5lcmd5IFJlZmluZW1lbnQpKio6IEENCiAgICAgICAgc3VpdGUgb2YgYmlvbW9sZWN1bGFyIHNpbXVsYXRpb24gcHJvZ3JhbXMuIEl0IGlzIHVzZWQgcHJpbWFyaWx5DQogICAgICAgIGZvciBtb2xlY3VsYXIgZHluYW1pY3Mgc2ltdWxhdGlvbnMgb2YgcHJvdGVpbnMgYW5kIG51Y2xlaWMNCiAgICAgICAgYWNpZHMuDQoyLiAgKiphcHB0YWluZXIvMS4xLjkqKg0KICAgIC0gICAqKkFwcHRhaW5lcioqOiBGb3JtZXJseSBrbm93biBhcyBTaW5ndWxhcml0eSwgaXQgaXMgYSBjb250YWluZXINCiAgICAgICAgcGxhdGZvcm0gZGVzaWduZWQgZm9yIEhQQyBlbnZpcm9ubWVudHMuIEl0IGFsbG93cyB1c2VycyB0bw0KICAgICAgICBjcmVhdGUgYW5kIHJ1biBjb250YWluZXJzIHRoYXQgcGFja2FnZSB1cCBhcHBsaWNhdGlvbnMgYW5kIHRoZWlyDQogICAgICAgIGRlcGVuZGVuY2llcyBpbiBhIHBvcnRhYmxlIG1hbm5lci4NCjMuICAqKmNvbmRhKioNCiAgICAtICAgKipDb25kYSoqOiBBbiBvcGVuLXNvdXJjZSBwYWNrYWdlIG1hbmFnZW1lbnQgYW5kIGVudmlyb25tZW50DQogICAgICAgIG1hbmFnZW1lbnQgc3lzdGVtLiBJdCBhbGxvd3MgeW91IHRvIGNyZWF0ZSBpc29sYXRlZCBlbnZpcm9ubWVudHMNCiAgICAgICAgYW5kIGluc3RhbGwgcGFja2FnZXMgZnJvbSB0aGUgQ29uZGEgcmVwb3NpdG9yeS4gSXQgaXMNCiAgICAgICAgcGFydGljdWxhcmx5IHBvcHVsYXIgZm9yIG1hbmFnaW5nIFB5dGhvbiBlbnZpcm9ubWVudHMgYW5kDQogICAgICAgIHBhY2thZ2VzLg0KNC4gICoqZ2F1c3NpYW4vZzE2YzAyKioNCiAgICAtICAgKipHYXVzc2lhbioqOiBTb2Z0d2FyZSB1c2VkIGZvciBjb21wdXRhdGlvbmFsIGNoZW1pc3RyeS4NCiAgICAgICAgR2F1c3NpYW4gMTYgaXMgYSB2ZXJzaW9uIG9mIHRoZSBHYXVzc2lhbiBzb2Z0d2FyZSBzdWl0ZSwgd2hpY2gNCiAgICAgICAgcGVyZm9ybXMgZWxlY3Ryb25pYyBzdHJ1Y3R1cmUgY2FsY3VsYXRpb25zLiBJdCBpcyB3aWRlbHkgdXNlZCBpbg0KICAgICAgICBjaGVtaXN0cnkgZm9yIHByZWRpY3RpbmcgdGhlIGVuZXJnaWVzLCBtb2xlY3VsYXIgc3RydWN0dXJlcywgYW5kDQogICAgICAgIHZpYnJhdGlvbmFsIGZyZXF1ZW5jaWVzIG9mIG1vbGVjdWxhciBzeXN0ZW1zLg0KNS4gICoqanVsaWEvMS45LjIqKg0KICAgIC0gICAqKkp1bGlhKio6IEEgaGlnaC1sZXZlbCwgaGlnaC1wZXJmb3JtYW5jZSBwcm9ncmFtbWluZyBsYW5ndWFnZQ0KICAgICAgICBmb3IgdGVjaG5pY2FsIGNvbXB1dGluZy4gSXQgaXMgZGVzaWduZWQgZm9yIG51bWVyaWNhbCBhbmQNCiAgICAgICAgc2NpZW50aWZpYyBjb21wdXRpbmcsIGFuZCB2ZXJzaW9uIDEuOS4yIGlzIGEgc3BlY2lmaWMgcmVsZWFzZSBvZg0KICAgICAgICB0aGlzIGxhbmd1YWdlLg0KNi4gICoqbGFtbXBzL21heTIyKioNCiAgICAtICAgKipMQU1NUFMgKExhcmdlLXNjYWxlIEF0b21pYy9Nb2xlY3VsYXIgTWFzc2l2ZWx5IFBhcmFsbGVsDQogICAgICAgIFNpbXVsYXRvcikqKjogQSBjbGFzc2ljYWwgbW9sZWN1bGFyIGR5bmFtaWNzIGNvZGUgdGhhdCBjYW4gbW9kZWwNCiAgICAgICAgYW4gZW5zZW1ibGUgb2YgcGFydGljbGVzIGluIGEgbGlxdWlkLCBzb2xpZCwgb3IgZ2FzZW91cyBzdGF0ZS4NCiAgICAgICAgTEFNTVBTIGlzIHdpZGVseSB1c2VkIGluIG1hdGVyaWFscyBzY2llbmNlLCBjaGVtaXN0cnksIGFuZA0KICAgICAgICBwaHlzaWNzLg0KNy4gICoqc3BhY2sqKg0KICAgIC0gICAqKlNwYWNrKio6IEEgZmxleGlibGUgcGFja2FnZSBtYW5hZ2VyIHRoYXQgc3VwcG9ydHMgbXVsdGlwbGUNCiAgICAgICAgdmVyc2lvbnMgYW5kIGNvbmZpZ3VyYXRpb25zIG9mIHNvZnR3YXJlLiBJdCBpcyBwYXJ0aWN1bGFybHkNCiAgICAgICAgZGVzaWduZWQgZm9yIEhQQyBlbnZpcm9ubWVudHMgYW5kIGFsbG93cyB1c2VycyB0byBlYXNpbHkNCiAgICAgICAgaW5zdGFsbCwgbWFuYWdlLCBhbmQgc3dhcCBzb2Z0d2FyZSBwYWNrYWdlcy4NCg0KVG8gdXNlIHRoZSBOVklESUEgTlZIUEMgMjMuNyBjb21waWxlciBzdWl0ZSBvbiBIUEMgc3lzdGVtOg0KDQojIyMgU3RlcCAxOiBMb2FkIHRoZSBOVkhQQyBNb2R1bGUNCg0KTG9hZCB0aGUgTlZJRElBIE5WSFBDIG1vZHVsZSAtc2V0cyB1cCBlbnZpcm9ubWVudCB3aXRoIHRoZSBuZWNlc3NhcnkNCnBhdGhzIGFuZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMuDQoNCmBgYCBzaA0KbW9kdWxlIGxvYWQgbnZpZGlhL252aHBjLzIzLjcNCmBgYA0KDQpWZXJpZnkgdGhhdCB0aGUgbW9kdWxlIGhhcyBiZWVuIGxvYWRlZCBjb3JyZWN0bHkgYnkgY2hlY2tpbmcgdGhlDQpjdXJyZW50bHkgbG9hZGVkIG1vZHVsZXM6DQoNCmBgYCBzaA0KbW9kdWxlIGxpc3QNCmBgYA0KDQojIyMgU3RlcCAyOiBDb21waWxlIG15IENvZGUNCg0KT25jZSBsb2FkZWQsdXNlIHRoZSBOVkhQQyBjb21waWxlcnMgdG8gY29tcGlsZSBjb2RlLiBOVkhQQyBpbmNsdWRlcw0Kc2V2ZXJhbCBjb21waWxlcnM6DQoNCi0gICBgbnZjYCBmb3IgQw0KLSAgIGBudmMrK2AgZm9yIEMrKw0KLSAgIGBudmZvcnRyYW5gIGZvciBGb3J0cmFuDQoNCkhlcmUgYXJlIGV4YW1wbGVzIG9mIGhvdyB0byBjb21waWxlIEMsIEMrKywgYW5kIEZvcnRyYW4gY29kZToNCg0KIyMjIyBDIENvZGUgQ29tcGlsYXRpb24NCg0KYGBgIHNoDQpudmMgLW8gbXlfcHJvZ3JhbSBteV9wcm9ncmFtLmMNCmBgYA0KDQojIyMjIEMrKyBDb2RlIENvbXBpbGF0aW9uDQoNCmBgYCBzaA0KbnZjKysgLW8gbXlfcHJvZ3JhbSBteV9wcm9ncmFtLmNwcA0KYGBgDQoNCiMjIyMgRm9ydHJhbiBDb2RlIENvbXBpbGF0aW9uDQoNCmBgYCBzaA0KbnZmb3J0cmFuIC1vIG15X3Byb2dyYW0gbXlfcHJvZ3JhbS5mOTANCmBgYA0KDQojIyMgU3RlcCAzOiBSdW4gbXkgUHJvZ3JhbQ0KDQpBZnRlciBjb21waWxpbmcgY29kZSBydW4gdGhlIGV4ZWN1dGFibGUgYXMgSSB3b3VsZCBhbnkgb3RoZXIgcHJvZ3JhbToNCg0KYGBgIHNoDQouL215X3Byb2dyYW0NCmBgYA0KDQojIyMgU3RlcCA0OiBVc2luZyBDVURBDQoNCklmIGNvZGUgdXRpbGl6ZXMgQ1VEQSBmb3IgR1BVIGFjY2VsZXJhdGlvbiwgTlZIUEMgc3VwcG9ydHMgQ1VEQQ0KZGlyZWN0bHkuIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiBjb21waWxpbmcgYSBDVURBIHByb2dyYW06DQoNCmBgYCBzaA0KbnZjKysgLW8gbXlfY3VkYV9wcm9ncmFtIG15X2N1ZGFfcHJvZ3JhbS5jdQ0KYGBgDQoNCiMjIyBFeGFtcGxlIFdvcmtmbG93DQoNCkV4YW1wbGUgd29ya2Zsb3c6DQoNCjEuICAqKkxvYWQgdGhlIE5WSFBDIG1vZHVsZToqKg0KDQogICAgYGBgIHNoDQogICAgbW9kdWxlIGxvYWQgbnZpZGlhL252aHBjLzIzLjcNCiAgICBgYGANCg0KMi4gICoqQ29tcGlsZSBDIHByb2dyYW06KioNCg0KICAgIGBgYCBzaA0KICAgIG52YyAtbyBoZWxsb193b3JsZCBoZWxsb193b3JsZC5jDQogICAgYGBgDQoNCjMuICAqKlJ1biBjb21waWxlZCBwcm9ncmFtOioqDQoNCiAgICBgYGAgc2gNCiAgICAuL2hlbGxvX3dvcmxkDQogICAgYGBgDQoNCiMjIyBBZGRpdGlvbmFsIFRvb2xzIGFuZCBMaWJyYXJpZXMNCg0KTlZIUEMgYWxzbyBjb21lcyB3aXRoIGFkZGl0aW9uYWwgdG9vbHMgYW5kIGxpYnJhcmllcywgc3VjaCBhczoNCg0KLSAgICoqTWF0aCBMaWJyYXJpZXM6KiogT3B0aW1pemVkIGxpYnJhcmllcyBsaWtlIGN1QkxBUywgY3VGRlQsIGV0Yy4NCi0gICAqKkRlYnVnZ2VyIGFuZCBQcm9maWxlcjoqKiBUb29scyBsaWtlIGBjdWRhLWdkYmAgYW5kDQogICAgYG5zaWdodC1jb21wdXRlYCBmb3IgZGVidWdnaW5nIGFuZCBwcm9maWxpbmcgR1BVIGFwcGxpY2F0aW9ucy4NCg0KVG8gYWNjZXNzIHRoZSBkb2N1bWVudGF0aW9uIGFuZCBhZGRpdGlvbmFsIGhlbHAgZm9yIE5WSFBDIHRvb2xzLCBjYW4gdXNlDQp0aGUgYG1hbmAgY29tbWFuZCBvciByZWZlciB0byB0aGUgb25saW5lIGRvY3VtZW50YXRpb24gcHJvdmlkZWQgYnkNCk5WSURJQS4NCg0KIyMjIEV4YW1wbGUgQ1VEQSBQcm9ncmFtIENvbXBpbGF0aW9uDQoNCkhlcmXigJlzIGEgYnJpZWYgZXhhbXBsZSBvZiBjb21waWxpbmcgYSBDVURBIHByb2dyYW06DQoNCioqaGVsbG9fd29ybGQuY3U6KioNCg0KYGBgIGNwcA0KI2luY2x1ZGUgPHN0ZGlvLmg+DQoNCl9fZ2xvYmFsX18gdm9pZCBoZWxsb0Zyb21HUFUoKSB7DQogICAgcHJpbnRmKCJIZWxsbyBXb3JsZCBmcm9tIEdQVSFcbiIpOw0KfQ0KDQppbnQgbWFpbigpIHsNCiAgICBoZWxsb0Zyb21HUFU8PDwxLCAxPj4+KCk7DQogICAgY3VkYURldmljZVN5bmNocm9uaXplKCk7DQogICAgcmV0dXJuIDA7DQp9DQpgYGANCg0KKipDb21waWxpbmcgYW5kIFJ1bm5pbmc6KioNCg0KMS4gICoqQ29tcGlsZToqKg0KDQogICAgYGBgIHNoDQogICAgbnZjKysgLW8gaGVsbG9fd29ybGQgaGVsbG9fd29ybGQuY3UNCiAgICBgYGANCg0KMi4gICoqUnVuOioqDQoNCiAgICBgYGAgc2gNCiAgICAuL2hlbGxvX3dvcmxkDQogICAgYGBgDQoNCkNyZWF0aW5nIGEgUXVhbnR1bSBHZW5lcmF0aXZlIEFkdmVyc2FyaWFsIE5ldHdvcmsgKFFHQU4pIGZvciBhbm9tYWx5DQpkZXRlY3Rpb24gaW4gR2VvZ3JhcGhpYyBJbmZvcm1hdGlvbiBTeXN0ZW1zIChHSVMpIGlzIGFuIGFkdmFuY2VkIHRhc2sNCnRoYXQgaW52b2x2ZXMgaW50ZWdyYXRpbmcgY2xhc3NpY2FsIGFuZCBxdWFudHVtIGNvbXB1dGluZyByZXNvdXJjZXMuDQpIZXJl4oCZcyBhIGRldGFpbGVkIGd1aWRlIG9uIGhvdyB0byBzZXQgdXAgdGhlIGVudmlyb25tZW50IGFuZCB0aGUgdG9vbHMgSQ0KY291bGQgdXNlOg0KDQojIyMgU3RlcCAxOiBDaG9vc2UgdGhlIFJpZ2h0IEVudmlyb25tZW50IGFuZCBUb29scw0KDQoxLiAgKipRdWFudHVtIENvbXB1dGluZyBTREsqKjogVXNlIElCTeKAmXMgUWlza2l0IG9yIEdvb2dsZeKAmXMgQ2lycSBmb3INCiAgICBxdWFudHVtIGNvbXB1dGluZyBjb21wb25lbnRzLg0KMi4gICoqQ2xhc3NpY2FsIENvbXB1dGluZyBFbnZpcm9ubWVudCoqOiBVc2UgUHl0aG9uIHdpdGggbGlicmFyaWVzIHN1Y2gNCiAgICBhcyBUZW5zb3JGbG93IG9yIFB5VG9yY2ggZm9yIGNsYXNzaWNhbCBtYWNoaW5lIGxlYXJuaW5nIGNvbXBvbmVudHMuDQozLiAgKipHSVMgVG9vbHMqKjogVXNlIGxpYnJhcmllcyBsaWtlIEdlb1BhbmRhcywgR0RBTCwgb3Igb3RoZXIgcmVsZXZhbnQNCiAgICBHSVMgdG9vbHMgaW4gUHl0aG9uLg0KNC4gICoqQ29tcGlsZXIqKjogWW91IGNhbiB1c2UgdGhlIE5WSURJQSBOVkhQQyBjb21waWxlciBmb3Igb3B0aW1pemluZw0KICAgIGNsYXNzaWNhbCBjb2RlLCBidXQgUHl0aG9uIGVudmlyb25tZW50cyB1c3VhbGx5IHVzZSBzdGFuZGFyZA0KICAgIGludGVycHJldGVycy4NCg0KIyMjIFN0ZXAgMjogU2V0IFVwIHRoZSBDbGFzc2ljYWwgRW52aXJvbm1lbnQNCg0KRmlyc3QsIGVuc3VyZSB0aGF0IEkgaGF2ZSBQeXRob24gYW5kIG5lY2Vzc2FyeSBwYWNrYWdlcyBpbnN0YWxsZWQuIFVzaW5nDQpgY29uZGFgIHRvIGNyZWF0ZSBhbiBpc29sYXRlZCBlbnZpcm9ubWVudCBpcyByZWNvbW1lbmRlZC4NCg0KIyMjIyAxLiBJbnN0YWxsIEFuYWNvbmRhIG9yIE1pbmljb25kYQ0KDQpEb3dubG9hZCBhbmQgaW5zdGFsbCBBbmFjb25kYSBvciBNaW5pY29uZGEgZnJvbSB0aGUgb2ZmaWNpYWwgd2Vic2l0ZS4NCg0KIyMjIyAyLiBDcmVhdGUgYSBDb25kYSBFbnZpcm9ubWVudA0KDQpgYGAgc2gNCmNvbmRhIGNyZWF0ZSAtbiBxZ2FuX2VudiBweXRob249My45DQpjb25kYSBhY3RpdmF0ZSBxZ2FuX2Vudg0KYGBgDQoNCiMjIyMgMy4gSW5zdGFsbCBSZXF1aXJlZCBQYWNrYWdlcw0KDQpgYGAgc2gNCmNvbmRhIGluc3RhbGwgbnVtcHkgcGFuZGFzIGdlb3BhbmRhcyBnZGFsIG1hdHBsb3RsaWINCnBpcCBpbnN0YWxsIHFpc2tpdCB0ZW5zb3JmbG93IHRvcmNoIHRvcmNodmlzaW9uDQpgYGANCg0KIyMjIFN0ZXAgMzogU2V0IFVwIFF1YW50dW0gQ29tcHV0aW5nIFNESw0KDQojIyMjIDEuIEluc3RhbGwgUWlza2l0DQoNCklmIHVzaW5nIElCTeKAmXMgUWlza2l0Og0KDQpgYGAgc2gNCnBpcCBpbnN0YWxsIHFpc2tpdA0KYGBgDQoNCkZvciBDaXJxIChHb29nbGUpOg0KDQpgYGAgc2gNCnBpcCBpbnN0YWxsIGNpcnENCmBgYA0KDQojIyMgU3RlcCA0OiBJbnRlZ3JhdGUgQ2xhc3NpY2FsIGFuZCBRdWFudHVtIENvbXBvbmVudHMNCg0KSGVyZeKAmXMgYW4gZXhhbXBsZSBvZiBhIHdvcmtmbG93IGludGVncmF0aW5nIGNsYXNzaWNhbCBhbmQgcXVhbnR1bQ0KY29tcG9uZW50cyBmb3IgYSBRR0FOOg0KDQojIyMjIDEuIENsYXNzaWNhbCBHQU4gZm9yIEdJUyBEYXRhDQoNCkNyZWF0ZSBhIGNsYXNzaWNhbCBHQU4gZm9yIGdlbmVyYXRpbmcgR0lTIGRhdGEgYW5vbWFsaWVzLiBCZWxvdyBpcyBhDQpzaW1wbGlmaWVkIGV4YW1wbGUgdXNpbmcgUHlUb3JjaDoNCg0KYGBgIHB5dGhvbg0KaW1wb3J0IHRvcmNoDQppbXBvcnQgdG9yY2gubm4gYXMgbm4NCmltcG9ydCB0b3JjaC5vcHRpbSBhcyBvcHRpbQ0KaW1wb3J0IGdlb3BhbmRhcyBhcyBncGQNCg0KIyBFeGFtcGxlOiBTaW1wbGUgR0FOIGNvbXBvbmVudHMNCmNsYXNzIEdlbmVyYXRvcihubi5Nb2R1bGUpOg0KICAgIGRlZiBfX2luaXRfXyhzZWxmKToNCiAgICAgICAgc3VwZXIoR2VuZXJhdG9yLCBzZWxmKS5fX2luaXRfXygpDQogICAgICAgIHNlbGYubWFpbiA9IG5uLlNlcXVlbnRpYWwoDQogICAgICAgICAgICBubi5MaW5lYXIoMTAwLCAyNTYpLA0KICAgICAgICAgICAgbm4uUmVMVSgpLA0KICAgICAgICAgICAgbm4uTGluZWFyKDI1NiwgNTEyKSwNCiAgICAgICAgICAgIG5uLlJlTFUoKSwNCiAgICAgICAgICAgIG5uLkxpbmVhcig1MTIsIDEwMjQpLA0KICAgICAgICAgICAgbm4uVGFuaCgpDQogICAgICAgICkNCiAgICANCiAgICBkZWYgZm9yd2FyZChzZWxmLCBpbnB1dCk6DQogICAgICAgIHJldHVybiBzZWxmLm1haW4oaW5wdXQpDQoNCmNsYXNzIERpc2NyaW1pbmF0b3Iobm4uTW9kdWxlKToNCiAgICBkZWYgX19pbml0X18oc2VsZik6DQogICAgICAgIHN1cGVyKERpc2NyaW1pbmF0b3IsIHNlbGYpLl9faW5pdF9fKCkNCiAgICAgICAgc2VsZi5tYWluID0gbm4uU2VxdWVudGlhbCgNCiAgICAgICAgICAgIG5uLkxpbmVhcigxMDI0LCA1MTIpLA0KICAgICAgICAgICAgbm4uTGVha3lSZUxVKDAuMiksDQogICAgICAgICAgICBubi5MaW5lYXIoNTEyLCAyNTYpLA0KICAgICAgICAgICAgbm4uTGVha3lSZUxVKDAuMiksDQogICAgICAgICAgICBubi5MaW5lYXIoMjU2LCAxKSwNCiAgICAgICAgICAgIG5uLlNpZ21vaWQoKQ0KICAgICAgICApDQogICAgDQogICAgZGVmIGZvcndhcmQoc2VsZiwgaW5wdXQpOg0KICAgICAgICByZXR1cm4gc2VsZi5tYWluKGlucHV0KQ0KDQojIEluaXRpYWxpemUgbW9kZWxzDQpHID0gR2VuZXJhdG9yKCkNCkQgPSBEaXNjcmltaW5hdG9yKCkNCg0KIyBPcHRpbWl6ZXJzIGFuZCBsb3NzIGZ1bmN0aW9uDQpvcHRpbWl6ZXJHID0gb3B0aW0uQWRhbShHLnBhcmFtZXRlcnMoKSwgbHI9MC4wMDAyKQ0Kb3B0aW1pemVyRCA9IG9wdGltLkFkYW0oRC5wYXJhbWV0ZXJzKCksIGxyPTAuMDAwMikNCmNyaXRlcmlvbiA9IG5uLkJDRUxvc3MoKQ0KDQojIFRyYWluaW5nIGxvb3AgcGxhY2Vob2xkZXINCmZvciBlcG9jaCBpbiByYW5nZShlcG9jaHMpOg0KICAgICMgVHJhaW5pbmcgY29kZSBoZXJlDQogICAgcGFzcw0KYGBgDQoNCiMjIyMgMi4gSW50ZWdyYXRlIFF1YW50dW0gQ29tcG9uZW50DQoNClVzaW5nIFFpc2tpdCBmb3IgcXVhbnR1bSBwYXJ0cyBvZiBRR0FOOg0KDQpgYGAgcHl0aG9uDQpmcm9tIHFpc2tpdCBpbXBvcnQgUXVhbnR1bUNpcmN1aXQsIHRyYW5zcGlsZSwgQWVyLCBleGVjdXRlDQpmcm9tIHFpc2tpdC52aXN1YWxpemF0aW9uIGltcG9ydCBwbG90X2hpc3RvZ3JhbQ0KDQojIFF1YW50dW0gZ2VuZXJhdG9yDQpkZWYgcXVhbnR1bV9nZW5lcmF0b3IoKToNCiAgICBxYyA9IFF1YW50dW1DaXJjdWl0KDEsIDEpDQogICAgcWMuaCgwKQ0KICAgIHFjLm1lYXN1cmUoMCwgMCkNCiAgICByZXR1cm4gcWMNCg0KIyBTaW11bGF0ZSB0aGUgcXVhbnR1bSBjaXJjdWl0DQpiYWNrZW5kID0gQWVyLmdldF9iYWNrZW5kKCdxYXNtX3NpbXVsYXRvcicpDQpxYyA9IHF1YW50dW1fZ2VuZXJhdG9yKCkNCmpvYiA9IGV4ZWN1dGUocWMsIGJhY2tlbmQsIHNob3RzPTEwMDApDQpyZXN1bHQgPSBqb2IucmVzdWx0KCkNCmNvdW50cyA9IHJlc3VsdC5nZXRfY291bnRzKHFjKQ0KcHJpbnQoY291bnRzKQ0KDQpwbG90X2hpc3RvZ3JhbShjb3VudHMpDQpgYGANCg0KIyMjIFN0ZXAgNTogSW50ZWdyYXRlIENsYXNzaWNhbCBhbmQgUXVhbnR1bSBDb21wb25lbnRzIGluIHRoZSBRR0FODQoNCkNvbWJpbmUgY2xhc3NpY2FsIGFuZCBxdWFudHVtIGNvbXBvbmVudHMuIEhlcmXigJlzIGEgc2ltcGxpZmllZCBwc2V1ZG9jb2RlDQppbnRlZ3JhdGlvbjoNCg0KYGBgIHB5dGhvbg0KIyBFeGFtcGxlIG9mIGludGVncmF0aW5nIHF1YW50dW0tZ2VuZXJhdGVkIGRhdGEgd2l0aCBjbGFzc2ljYWwgR0FOIHRyYWluaW5nDQpmb3IgZXBvY2ggaW4gcmFuZ2UoZXBvY2hzKToNCiAgICBmb3IgaSwgZGF0YSBpbiBlbnVtZXJhdGUoZGF0YWxvYWRlcik6DQogICAgICAgICMgUXVhbnR1bSBkYXRhIGdlbmVyYXRpb24NCiAgICAgICAgcWMgPSBxdWFudHVtX2dlbmVyYXRvcigpDQogICAgICAgIGpvYiA9IGV4ZWN1dGUocWMsIGJhY2tlbmQsIHNob3RzPTEpDQogICAgICAgIHJlc3VsdCA9IGpvYi5yZXN1bHQoKQ0KICAgICAgICBxdWFudHVtX2RhdGEgPSBsaXN0KHJlc3VsdC5nZXRfY291bnRzKHFjKS5rZXlzKCkpWzBdDQoNCiAgICAgICAgIyBDb252ZXJ0IHF1YW50dW0gZGF0YSB0byBpbnB1dCBmb3JtYXQNCiAgICAgICAgcXVhbnR1bV9pbnB1dCA9IHRvcmNoLlRlbnNvcihbZmxvYXQoYml0KSBmb3IgYml0IGluIHF1YW50dW1fZGF0YV0pDQoNCiAgICAgICAgIyBDbGFzc2ljYWwgR0FOIHRyYWluaW5nIHN0ZXBzDQogICAgICAgIHJlYWxfZGF0YSA9IGRhdGFbMF0NCiAgICAgICAgZmFrZV9kYXRhID0gRyhxdWFudHVtX2lucHV0KQ0KDQogICAgICAgICMgVXBkYXRlIERpc2NyaW1pbmF0b3INCiAgICAgICAgRC56ZXJvX2dyYWQoKQ0KICAgICAgICByZWFsX291dHB1dCA9IEQocmVhbF9kYXRhKQ0KICAgICAgICBmYWtlX291dHB1dCA9IEQoZmFrZV9kYXRhLmRldGFjaCgpKQ0KICAgICAgICBsb3NzRCA9IGNyaXRlcmlvbihyZWFsX291dHB1dCwgdG9yY2gub25lc19saWtlKHJlYWxfb3V0cHV0KSkgKyBjcml0ZXJpb24oZmFrZV9vdXRwdXQsIHRvcmNoLnplcm9zX2xpa2UoZmFrZV9vdXRwdXQpKQ0KICAgICAgICBsb3NzRC5iYWNrd2FyZCgpDQogICAgICAgIG9wdGltaXplckQuc3RlcCgpDQoNCiAgICAgICAgIyBVcGRhdGUgR2VuZXJhdG9yDQogICAgICAgIEcuemVyb19ncmFkKCkNCiAgICAgICAgZmFrZV9vdXRwdXQgPSBEKGZha2VfZGF0YSkNCiAgICAgICAgbG9zc0cgPSBjcml0ZXJpb24oZmFrZV9vdXRwdXQsIHRvcmNoLm9uZXNfbGlrZShmYWtlX291dHB1dCkpDQogICAgICAgIGxvc3NHLmJhY2t3YXJkKCkNCiAgICAgICAgb3B0aW1pemVyRy5zdGVwKCkNCg0KICAgICAgICAjIFByaW50IGxvc3NlcyAob3B0aW9uYWwpDQogICAgICAgIHByaW50KGYnRXBvY2ggW3tlcG9jaH0ve2Vwb2Noc31dLCBTdGVwIFt7aX0ve2xlbihkYXRhbG9hZGVyKX1dLCBMb3NzIEQ6IHtsb3NzRC5pdGVtKCl9LCBMb3NzIEc6IHtsb3NzRy5pdGVtKCl9JykNCmBgYA0KDQojIyMgQ29uY2x1c2lvbg0KDQoxLiAgKipTZXQgdXAgdGhlIGVudmlyb25tZW50Kio6IEluc3RhbGwgbmVjZXNzYXJ5IHRvb2xzIHVzaW5nIGBjb25kYWANCiAgICBhbmQgYHBpcGAuDQoyLiAgKipDcmVhdGUgY2xhc3NpY2FsIGFuZCBxdWFudHVtIG1vZGVscyoqOiBVc2UgUHlUb3JjaCBmb3IgY2xhc3NpY2FsDQogICAgR0FOIGFuZCBRaXNraXQgZm9yIHF1YW50dW0gY29tcG9uZW50cy4NCjMuICAqKkludGVncmF0ZSBtb2RlbHMqKjogQ29tYmluZSBxdWFudHVtLWdlbmVyYXRlZCBkYXRhIHdpdGggY2xhc3NpY2FsDQogICAgR0FOIHRyYWluaW5nLg0KDQojIENyZWF0ZSBhIFFHQU4gKFF1YW50dW0gR2VuZXJhdGl2ZSBBZHZlcnNhcmlhbCBOZXR3b3JrKSBmb3IgYW5vbWFseSBkZXRlY3Rpb24gaW4gR0lTIChHZW9ncmFwaGljIEluZm9ybWF0aW9uIFN5c3RlbXMpIG9uIHRoZSBIUEMgc3lzdGVtIEkgbmVlZCB0byBsZXZlcmFnZSBib3RoIHF1YW50dW0gYW5kIGNsYXNzaWNhbCBjb21wdXRpbmcgcmVzb3VyY2VzIGVmZmVjdGl2ZWx5LiBIZXJlIGlzIGEgc3RlcC1ieS1zdGVwIGd1aWRlIHRvIHNldCB1cCBteSBlbnZpcm9ubWVudCBhbmQgY29tcGlsZS9ydW4gbXkgUUdBTiBvbiB0aGUgSFBDIHN5c3RlbToNCg0KIyMjIFN0ZXAgMTogTG9hZCBOZWNlc3NhcnkgTW9kdWxlcw0KDQpGaXJzdCwgbG9hZCB0aGUgbW9kdWxlcyBuZWNlc3NhcnkgZm9yIGVudmlyb25tZW50LiBUaGlzIGluY2x1ZGVzIFB5dGhvbiwNCmFueSByZXF1aXJlZCBxdWFudHVtIGNvbXB1dGluZyBTREtzLCBhbmQgb3RoZXIgbGlicmFyaWVzLg0KDQpgYGAgc2gNCm1vZHVsZSBsb2FkIGdjYy8xMy4yLjANCm1vZHVsZSBsb2FkIG52aWRpYS9udmhwYy8yMy43DQptb2R1bGUgbG9hZCBjb25kYQ0KYGBgDQoNCiMjIyBTdGVwIDI6IENyZWF0ZSBhbmQgQWN0aXZhdGUgYSBDb25kYSBFbnZpcm9ubWVudA0KDQpVc2UgQ29uZGEgdG8gbWFuYWdlIG15IFB5dGhvbiBlbnZpcm9ubWVudCwgd2hpY2ggd2lsbCBpbmNsdWRlIHRoZQ0KbmVjZXNzYXJ5IGxpYnJhcmllcyBmb3IgYm90aCBxdWFudHVtIGFuZCBjbGFzc2ljYWwgY29tcHV0aW5nLg0KDQpgYGAgc2gNCmNvbmRhIGNyZWF0ZSAtbiBxZ2FuX2VudiBweXRob249My45DQpjb25kYSBhY3RpdmF0ZSBxZ2FuX2Vudg0KYGBgDQoNCiMjIyBTdGVwIDM6IEluc3RhbGwgUmVxdWlyZWQgUHl0aG9uIFBhY2thZ2VzDQoNCldpdGggQ29uZGEgZW52aXJvbm1lbnQgYWN0aXZhdGVkLCBpbnN0YWxsIHRoZSBuZWNlc3NhcnkgUHl0aG9uIHBhY2thZ2VzLg0KDQpgYGAgc2gNCiMgSW5zdGFsbCBiYXNpYyBwYWNrYWdlcyBmb3IgR0lTIGFuZCBxdWFudHVtIGNvbXB1dGluZw0KY29uZGEgaW5zdGFsbCBudW1weSBwYW5kYXMgZ2VvcGFuZGFzIGdkYWwgbWF0cGxvdGxpYg0KcGlwIGluc3RhbGwgcWlza2l0IHRlbnNvcmZsb3cgdG9yY2ggdG9yY2h2aXNpb24NCmBgYA0KDQojIyMgU3RlcCA0OiBTZXQgVXAgdGhlIFF1YW50dW0gQ29tcHV0aW5nIEVudmlyb25tZW50DQoNCiMjIyMgSW5zdGFsbCBRaXNraXQgKElCTSdzIFF1YW50dW0gQ29tcHV0aW5nIFNESykNCg0KYGBgIHNoDQpwaXAgaW5zdGFsbCBxaXNraXQNCmBgYA0KDQojIyMgU3RlcCA1OiBEZXZlbG9wIHRoZSBRR0FODQoNCkNyZWF0ZSB0aGUgUUdBTiBieSBpbnRlZ3JhdGluZyBib3RoIGNsYXNzaWNhbCBhbmQgcXVhbnR1bSBjb21wb25lbnRzLg0KQmVsb3cgaXMgYW4gZXhhbXBsZSBzZXR1cC4NCg0KIyMjIyBDbGFzc2ljYWwgR0FOIGZvciBHSVMgRGF0YSAodXNpbmcgUHlUb3JjaCkNCg0KYGBgIHB5dGhvbg0KaW1wb3J0IHRvcmNoDQppbXBvcnQgdG9yY2gubm4gYXMgbm4NCmltcG9ydCB0b3JjaC5vcHRpbSBhcyBvcHRpbQ0KaW1wb3J0IGdlb3BhbmRhcyBhcyBncGQNCmltcG9ydCBudW1weSBhcyBucA0KDQpjbGFzcyBHZW5lcmF0b3Iobm4uTW9kdWxlKToNCiAgICBkZWYgX19pbml0X18oc2VsZik6DQogICAgICAgIHN1cGVyKEdlbmVyYXRvciwgc2VsZikuX19pbml0X18oKQ0KICAgICAgICBzZWxmLm1haW4gPSBubi5TZXF1ZW50aWFsKA0KICAgICAgICAgICAgbm4uTGluZWFyKDEwMCwgMjU2KSwNCiAgICAgICAgICAgIG5uLlJlTFUoKSwNCiAgICAgICAgICAgIG5uLkxpbmVhcigyNTYsIDUxMiksDQogICAgICAgICAgICBubi5SZUxVKCksDQogICAgICAgICAgICBubi5MaW5lYXIoNTEyLCAxMDI0KSwNCiAgICAgICAgICAgIG5uLlRhbmgoKQ0KICAgICAgICApDQogICAgDQogICAgZGVmIGZvcndhcmQoc2VsZiwgaW5wdXQpOg0KICAgICAgICByZXR1cm4gc2VsZi5tYWluKGlucHV0KQ0KDQpjbGFzcyBEaXNjcmltaW5hdG9yKG5uLk1vZHVsZSk6DQogICAgZGVmIF9faW5pdF9fKHNlbGYpOg0KICAgICAgICBzdXBlcihEaXNjcmltaW5hdG9yLCBzZWxmKS5fX2luaXRfXygpDQogICAgICAgIHNlbGYubWFpbiA9IG5uLlNlcXVlbnRpYWwoDQogICAgICAgICAgICBubi5MaW5lYXIoMTAyNCwgNTEyKSwNCiAgICAgICAgICAgIG5uLkxlYWt5UmVMVSgwLjIpLA0KICAgICAgICAgICAgbm4uTGluZWFyKDUxMiwgMjU2KSwNCiAgICAgICAgICAgIG5uLkxlYWt5UmVMVSgwLjIpLA0KICAgICAgICAgICAgbm4uTGluZWFyKDI1NiwgMSksDQogICAgICAgICAgICBubi5TaWdtb2lkKCkNCiAgICAgICAgKQ0KICAgIA0KICAgIGRlZiBmb3J3YXJkKHNlbGYsIGlucHV0KToNCiAgICAgICAgcmV0dXJuIHNlbGYubWFpbihpbnB1dCkNCg0KIyBJbml0aWFsaXplIG1vZGVscw0KRyA9IEdlbmVyYXRvcigpDQpEID0gRGlzY3JpbWluYXRvcigpDQoNCiMgT3B0aW1pemVycyBhbmQgbG9zcyBmdW5jdGlvbg0Kb3B0aW1pemVyRyA9IG9wdGltLkFkYW0oRy5wYXJhbWV0ZXJzKCksIGxyPTAuMDAwMikNCm9wdGltaXplckQgPSBvcHRpbS5BZGFtKEQucGFyYW1ldGVycygpLCBscj0wLjAwMDIpDQpjcml0ZXJpb24gPSBubi5CQ0VMb3NzKCkNCg0KIyBEdW1teSB0cmFpbmluZyBsb29wIHBsYWNlaG9sZGVyDQplcG9jaHMgPSAxMDANCmZvciBlcG9jaCBpbiByYW5nZShlcG9jaHMpOg0KICAgICMgVHJhaW5pbmcgY29kZSBoZXJlDQogICAgcGFzcw0KYGBgDQoNCiMjIyMgSW50ZWdyYXRlIFF1YW50dW0gQ29tcG9uZW50IHVzaW5nIFFpc2tpdA0KDQpgYGAgcHl0aG9uDQpmcm9tIHFpc2tpdCBpbXBvcnQgUXVhbnR1bUNpcmN1aXQsIEFlciwgZXhlY3V0ZQ0KZnJvbSBxaXNraXQudmlzdWFsaXphdGlvbiBpbXBvcnQgcGxvdF9oaXN0b2dyYW0NCg0KZGVmIHF1YW50dW1fZ2VuZXJhdG9yKCk6DQogICAgcWMgPSBRdWFudHVtQ2lyY3VpdCgxLCAxKQ0KICAgIHFjLmgoMCkNCiAgICBxYy5tZWFzdXJlKDAsIDApDQogICAgcmV0dXJuIHFjDQoNCmJhY2tlbmQgPSBBZXIuZ2V0X2JhY2tlbmQoJ3Fhc21fc2ltdWxhdG9yJykNCnFjID0gcXVhbnR1bV9nZW5lcmF0b3IoKQ0Kam9iID0gZXhlY3V0ZShxYywgYmFja2VuZCwgc2hvdHM9MTAwMCkNCnJlc3VsdCA9IGpvYi5yZXN1bHQoKQ0KY291bnRzID0gcmVzdWx0LmdldF9jb3VudHMocWMpDQpwcmludChjb3VudHMpDQoNCnBsb3RfaGlzdG9ncmFtKGNvdW50cykNCmBgYA0KDQojIyMgU3RlcCA2OiBJbnRlZ3JhdGUgQ2xhc3NpY2FsIGFuZCBRdWFudHVtIENvbXBvbmVudHMgaW4gdGhlIFFHQU4NCg0KQ29tYmluZSBxdWFudHVtLWdlbmVyYXRlZCBkYXRhIHdpdGggY2xhc3NpY2FsIEdBTiB0cmFpbmluZzoNCg0KYGBgIHB5dGhvbg0KIyBFeGFtcGxlIG9mIGludGVncmF0aW5nIHF1YW50dW0tZ2VuZXJhdGVkIGRhdGEgd2l0aCBjbGFzc2ljYWwgR0FOIHRyYWluaW5nDQpmb3IgZXBvY2ggaW4gcmFuZ2UoZXBvY2hzKToNCiAgICBmb3IgaSwgZGF0YSBpbiBlbnVtZXJhdGUoZGF0YWxvYWRlcik6DQogICAgICAgICMgUXVhbnR1bSBkYXRhIGdlbmVyYXRpb24NCiAgICAgICAgcWMgPSBxdWFudHVtX2dlbmVyYXRvcigpDQogICAgICAgIGpvYiA9IGV4ZWN1dGUocWMsIGJhY2tlbmQsIHNob3RzPTEpDQogICAgICAgIHJlc3VsdCA9IGpvYi5yZXN1bHQoKQ0KICAgICAgICBxdWFudHVtX2RhdGEgPSBsaXN0KHJlc3VsdC5nZXRfY291bnRzKHFjKS5rZXlzKCkpWzBdDQoNCiAgICAgICAgIyBDb252ZXJ0IHF1YW50dW0gZGF0YSB0byBpbnB1dCBmb3JtYXQNCiAgICAgICAgcXVhbnR1bV9pbnB1dCA9IHRvcmNoLlRlbnNvcihbZmxvYXQoYml0KSBmb3IgYml0IGluIHF1YW50dW1fZGF0YV0pDQoNCiAgICAgICAgIyBDbGFzc2ljYWwgR0FOIHRyYWluaW5nIHN0ZXBzDQogICAgICAgIHJlYWxfZGF0YSA9IGRhdGFbMF0NCiAgICAgICAgZmFrZV9kYXRhID0gRyhxdWFudHVtX2lucHV0KQ0KDQogICAgICAgICMgVXBkYXRlIERpc2NyaW1pbmF0b3INCiAgICAgICAgRC56ZXJvX2dyYWQoKQ0KICAgICAgICByZWFsX291dHB1dCA9IEQocmVhbF9kYXRhKQ0KICAgICAgICBmYWtlX291dHB1dCA9IEQoZmFrZV9kYXRhLmRldGFjaCgpKQ0KICAgICAgICBsb3NzRCA9IGNyaXRlcmlvbihyZWFsX291dHB1dCwgdG9yY2gub25lc19saWtlKHJlYWxfb3V0cHV0KSkgKyBjcml0ZXJpb24oZmFrZV9vdXRwdXQsIHRvcmNoLnplcm9zX2xpa2UoZmFrZV9vdXRwdXQpKQ0KICAgICAgICBsb3NzRC5iYWNrd2FyZCgpDQogICAgICAgIG9wdGltaXplckQuc3RlcCgpDQoNCiAgICAgICAgIyBVcGRhdGUgR2VuZXJhdG9yDQogICAgICAgIEcuemVyb19ncmFkKCkNCiAgICAgICAgZmFrZV9vdXRwdXQgPSBEKGZha2VfZGF0YSkNCiAgICAgICAgbG9zc0cgPSBjcml0ZXJpb24oZmFrZV9vdXRwdXQsIHRvcmNoLm9uZXNfbGlrZShmYWtlX291dHB1dCkpDQogICAgICAgIGxvc3NHLmJhY2t3YXJkKCkNCiAgICAgICAgb3B0aW1pemVyRy5zdGVwKCkNCg0KICAgICAgICAjIFByaW50IGxvc3NlcyAob3B0aW9uYWwpDQogICAgICAgIHByaW50KGYnRXBvY2ggW3tlcG9jaH0ve2Vwb2Noc31dLCBTdGVwIFt7aX0ve2xlbihkYXRhbG9hZGVyKX1dLCBMb3NzIEQ6IHtsb3NzRC5pdGVtKCl9LCBMb3NzIEc6IHtsb3NzRy5pdGVtKCl9JykNCmBgYA0KDQojIyMgU3RlcCA3OiBSdW4gb24gdGhlIEhQQyBTeXN0ZW0NCg0KQWZ0ZXIgc2V0dGluZyB1cCB0aGUgZW52aXJvbm1lbnQgYW5kIHdyaXRpbmcgY29kZSwgeW91IGNhbiBzdWJtaXQgam9iIHRvDQp0aGUgSFBDIHN5c3RlbSB1c2luZyBhIGpvYiBzY2hlZHVsZXIgbGlrZSBTTFVSTSBvciBQQlMuIEhlcmUgaXMgYW4NCmV4YW1wbGUgU0xVUk0gc2NyaXB0Og0KDQpgYGAgc2gNCiMhL2Jpbi9iYXNoDQojU0JBVENIIC0tam9iLW5hbWU9cWdhbl9naXMNCiNTQkFUQ0ggLS1vdXRwdXQ9cWdhbl9naXNfb3V0cHV0LnR4dA0KI1NCQVRDSCAtLW50YXNrcz0xDQojU0JBVENIIC0tY3B1cy1wZXItdGFzaz00DQojU0JBVENIIC0tbWVtPTE2Rw0KI1NCQVRDSCAtLXRpbWU9MjQ6MDA6MDANCiNTQkFUQ0ggLS1wYXJ0aXRpb249Z3B1DQojU0JBVENIIC0tZ3B1cz0xDQoNCm1vZHVsZSBsb2FkIGdjYy8xMy4yLjANCm1vZHVsZSBsb2FkIG52aWRpYS9udmhwYy8yMy43DQptb2R1bGUgbG9hZCBjb25kYQ0KDQpjb25kYSBhY3RpdmF0ZSBxZ2FuX2Vudg0KDQpweXRob24gbXlfcWdhbl9zY3JpcHQucHkNCmBgYA0KDQpTYXZlIHRoaXMgc2NyaXB0IGFzIGBzdWJtaXRfam9iLnNoYCBhbmQgc3VibWl0IGl0IHRvIHRoZSBqb2Igc2NoZWR1bGVyOg0KDQpgYGAgc2gNCnNiYXRjaCBzdWJtaXRfam9iLnNoDQpgYGANCg0KQnkgZm9sbG93aW5nIHRoZXNlIHN0ZXBzLCBJIHNob3VsZCBiZSBhYmxlIHRvIGNyZWF0ZSBhbmQgcnVuIGEgUUdBTiBmb3INCmFub21hbHkgZGV0ZWN0aW9uIGluIEdJUyBvbiBteSBIUEMgc3lzdGVtLCB1dGlsaXppbmcgYm90aCBxdWFudHVtIGFuZA0KY2xhc3NpY2FsIGNvbXB1dGluZyByZXNvdXJjZXMuDQoNCiMgTTMNCg0KVGhlIG1vZHVsZXMgYW5kIGNvbXBpbGVycyBhdmFpbGFibGUgb24gdGhlIEhQQyBzeXN0ZW0gKG0zKSBwcm92aWRlDQp2YXJpb3VzIHRvb2xzIGZvciBzY2llbnRpZmljIGNvbXB1dGluZywgZGV2ZWxvcG1lbnQsIGFuZCBhbmFseXNpcy4NCkhlcmXigJlzIGFuIGV4cGxhbmF0aW9uIG9mIGVhY2ggbW9kdWxlIGFuZCBjb21waWxlciBsaXN0ZWQ6DQoNCiMjIyBBcHBsaWNhdGlvbnMgTW9kdWxlcw0KDQoxLiAgKipSKioNCiAgICAtICAgKipWZXJzaW9uczogNC4yLjMsIDQuMy4wLCA0LjMuMiwgNC40LjAgKEQpKioNCiAgICAtICAgKipSKio6IEEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgYW5kIHNvZnR3YXJlIGVudmlyb25tZW50IHVzZWQgZm9yDQogICAgICAgIHN0YXRpc3RpY2FsIGNvbXB1dGluZyBhbmQgZ3JhcGhpY3MuIEl0IGlzIHdpZGVseSB1c2VkIGZvciBkYXRhDQogICAgICAgIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uLg0KMi4gICoqYW1wbC8yMDIzMTAzMSoqDQogICAgLSAgICoqQU1QTCoqOiBBIGNvbXByZWhlbnNpdmUgYW5kIHBvd2VyZnVsIGFsZ2VicmFpYyBtb2RlbGluZw0KICAgICAgICBsYW5ndWFnZSBmb3IgbGluZWFyIGFuZCBub25saW5lYXIgb3B0aW1pemF0aW9uIHByb2JsZW1zLg0KMy4gICoqY2hhcm1tL2M0N2IyKioNCiAgICAtICAgKipDSEFSTU0qKjogQSBwcm9ncmFtIGZvciBtYWNyb21vbGVjdWxhciBkeW5hbWljcyBhbmQgbWVjaGFuaWNzDQogICAgICAgIHNpbXVsYXRpb25zLCB1c2VkIGZvciBzdHVkeWluZyB0aGUgYmVoYXZpb3Igb2YgbW9sZWN1bGFyDQogICAgICAgIHN5c3RlbXMuDQo0LiAgKipkeW5hcmUqKg0KICAgIC0gICAqKlZlcnNpb25zOiA1LjQsIDUuNSAoRCkqKg0KICAgIC0gICAqKkR5bmFyZSoqOiBBIHNvZnR3YXJlIHBsYXRmb3JtIGZvciBoYW5kbGluZyBhIHdpZGUgY2xhc3Mgb2YNCiAgICAgICAgZWNvbm9taWMgbW9kZWxzLCBwYXJ0aWN1bGFybHkgdGhvc2UgaW52b2x2aW5nIGR5bmFtaWMgc3RvY2hhc3RpYw0KICAgICAgICBnZW5lcmFsIGVxdWlsaWJyaXVtIChEU0dFKSBhbmQgb3ZlcmxhcHBpbmcgZ2VuZXJhdGlvbnMgKE9MRykNCiAgICAgICAgbW9kZWxzLg0KNS4gICoqZ2F1c3NpYW4qKg0KICAgIC0gICAqKlZlcnNpb25zOiBnMTZjL2hhc3dlbGwsIGcxNmMvemVuMyAoRCkqKg0KICAgIC0gICAqKkdhdXNzaWFuKio6IFNvZnR3YXJlIGZvciBjb21wdXRhdGlvbmFsIGNoZW1pc3RyeSB1c2VkIGZvcg0KICAgICAgICBlbGVjdHJvbmljIHN0cnVjdHVyZSBtb2RlbGluZy4NCjYuICAqKmp1bGlhKioNCiAgICAtICAgKipWZXJzaW9uczogMS44LjUsIDEuOS4xIChEKSoqDQogICAgLSAgICoqSnVsaWEqKjogQSBoaWdoLWxldmVsLCBoaWdoLXBlcmZvcm1hbmNlIHByb2dyYW1taW5nIGxhbmd1YWdlDQogICAgICAgIGZvciB0ZWNobmljYWwgY29tcHV0aW5nLg0KNy4gICoqbmJvKioNCiAgICAtICAgKipWZXJzaW9uczogNy4wL2k0LCA3LjAvaTggKEQpKioNCiAgICAtICAgKipOQk8gKE5hdHVyYWwgQm9uZCBPcmJpdGFsKSoqOiBTb2Z0d2FyZSBmb3Igc3R1ZHlpbmcgY2hlbWljYWwNCiAgICAgICAgYm9uZGluZyBhbmQgZWxlY3Ryb24gZGVuc2l0eS4NCjguICAqKnNuaWQvNS4wKioNCiAgICAtICAgKipTTklEIChTdXBlck5vdmEgSWRlbnRpZmljYXRpb24pKio6IEEgdG9vbCBmb3IgY2xhc3NpZnlpbmcNCiAgICAgICAgc3VwZXJub3ZhIHNwZWN0cmEuDQo5LiAgKip2bWQvMS45LjMqKg0KICAgIC0gICAqKlZNRCAoVmlzdWFsIE1vbGVjdWxhciBEeW5hbWljcykqKjogU29mdHdhcmUgZm9yIG1vbGVjdWxhcg0KICAgICAgICBtb2RlbGluZyBhbmQgYmlvaW5mb3JtYXRpY3MuDQoxMC4gKiphbnN5cy9lbGVjdHJvbmljcyoqDQogICAgLSAgICoqVmVyc2lvbnM6IDIzLjEsIDIzLjIgKEQpKioNCiAgICAtICAgKipBTlNZUyBFbGVjdHJvbmljcyoqOiBTaW11bGF0aW9uIHNvZnR3YXJlIGZvciBlbGVjdHJvbmljcywNCiAgICAgICAgZWxlY3Ryb21hZ25ldGljLCBhbmQgdGhlcm1hbCBhbmFseXNpcy4NCjExLiAqKmNvbXNvbC82LjEqKg0KICAgIC0gICAqKkNPTVNPTCoqOiBBIG11bHRpcGh5c2ljcyBzaW11bGF0aW9uIHNvZnR3YXJlIGZvciBtb2RlbGluZyBhbmQNCiAgICAgICAgc2ltdWxhdGluZyBlbmdpbmVlcmluZyBwcm9ibGVtcy4NCjEyLiAqKmNvbmRhKioNCiAgICAtICAgKipDb25kYSoqOiBBbiBvcGVuLXNvdXJjZSBwYWNrYWdlIG1hbmFnZW1lbnQgYW5kIGVudmlyb25tZW50DQogICAgICAgIG1hbmFnZW1lbnQgc3lzdGVtIHRoYXQgYWxsb3dzIHVzZXJzIHRvIGNyZWF0ZSBpc29sYXRlZA0KICAgICAgICBlbnZpcm9ubWVudHMgYW5kIGluc3RhbGwgcGFja2FnZXMuDQoxMy4gKipjcGxleC8yMi4xLjEqKg0KICAgIC0gICAqKkNQTEVYKio6IE9wdGltaXphdGlvbiBzb2Z0d2FyZSBmb3Igc29sdmluZyBsaW5lYXIgcHJvZ3JhbW1pbmcsDQogICAgICAgIG1peGVkIGludGVnZXIgcHJvZ3JhbW1pbmcsIGFuZCBvdGhlciB0eXBlcyBvZiBvcHRpbWl6YXRpb24NCiAgICAgICAgcHJvYmxlbXMuDQoxNC4gKiplb2cvMy4yOC40KioNCiAgICAtICAgKipFT0cgKEV5ZSBvZiBHTk9NRSkqKjogQW4gaW1hZ2Ugdmlld2VyIGZvciB0aGUgR05PTUUgZGVza3RvcA0KICAgICAgICBlbnZpcm9ubWVudC4NCjE1LiAqKmdlZGl0LzMuMjguMSoqDQogICAgLSAgICoqR2VkaXQqKjogQSB0ZXh0IGVkaXRvciBmb3IgdGhlIEdOT01FIGRlc2t0b3AgZW52aXJvbm1lbnQuDQoxNi4gKipnYXVzc3ZpZXcvNi4wLjE2KioNCiAgICAtICAgKipHYXVzc1ZpZXcqKjogQSBncmFwaGljYWwgaW50ZXJmYWNlIGZvciBHYXVzc2lhbiBzb2Z0d2FyZSwNCiAgICAgICAgYWlkaW5nIGluIHRoZSBzZXR1cCBhbmQgYW5hbHlzaXMgb2YgY29tcHV0YXRpb25hbCBjaGVtaXN0cnkNCiAgICAgICAgY2FsY3VsYXRpb25zLg0KMTcuICoqbWF0aGVtYXRpY2EqKg0KICAgIC0gICAqKlZlcnNpb25zOiAxMy4zLjAsIHIyMDI0YSAoRCkqKg0KICAgIC0gICAqKk1hdGhlbWF0aWNhKio6IEEgY29tcHV0YXRpb25hbCBzb2Z0d2FyZSB1c2VkIGluIHNjaWVudGlmaWMsDQogICAgICAgIGVuZ2luZWVyaW5nLCBtYXRoZW1hdGljYWwgZmllbGRzLCBhbmQgb3RoZXIgYXJlYXMgZm9yDQogICAgICAgIGNvbXB1dGF0aW9ucyBhbmQgdmlzdWFsaXphdGlvbi4NCjE4LiAqKm1hdGxhYioqDQogICAgLSAgICoqVmVyc2lvbnM6IHIyMDIzYSwgcjIwMjRhIChEKSoqDQogICAgLSAgICoqTUFUTEFCKio6IEEgaGlnaC1sZXZlbCBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgZW52aXJvbm1lbnQNCiAgICAgICAgdXNlZCBmb3IgbnVtZXJpY2FsIGNvbXB1dGluZywgZGF0YSBhbmFseXNpcywgYW5kIGFsZ29yaXRobQ0KICAgICAgICBkZXZlbG9wbWVudC4NCjE5LiAqKm9yY2EvNS4wLjQqKg0KICAgIC0gICAqKk9SQ0EqKjogQW4gZWxlY3Ryb25pYyBzdHJ1Y3R1cmUgc29mdHdhcmUgcGFja2FnZSBmb3IgcXVhbnR1bQ0KICAgICAgICBjaGVtaXN0cnkgY2FsY3VsYXRpb25zLg0KMjAuICoqcS1jaGVtKioNCiAgICAtICAgKipWZXJzaW9uczogNS4yLjIsIDYuMC4yIChEKSoqDQogICAgLSAgICoqUS1DaGVtKio6IEEgc29mdHdhcmUgcGFja2FnZSBmb3IgY29tcHV0YXRpb25hbCBjaGVtaXN0cnksDQogICAgICAgIGZvY3VzaW5nIG9uIHF1YW50dW0gY2hlbWljYWwgY2FsY3VsYXRpb25zLg0KMjEuICoqc3RhdGEqKg0KICAgIC0gICAqKlZlcnNpb25zOiBtcC0xNywgbXAtMTggKEQpKioNCiAgICAtICAgKipTdGF0YSoqOiBBIHNvZnR3YXJlIGZvciBzdGF0aXN0aWNzIGFuZCBkYXRhIHNjaWVuY2UsIHVzZWQgZm9yDQogICAgICAgIGRhdGEgYW5hbHlzaXMsIGRhdGEgbWFuYWdlbWVudCwgYW5kIGdyYXBoaWNzLg0KMjIuICoqYWltYWxsLzE5LjEwLjEyKioNCiAgICAtICAgKipBSU1BbGwqKjogU29mdHdhcmUgZm9yIGFuYWx5emluZyBtb2xlY3VsYXIgd2F2ZSBmdW5jdGlvbnMgYW5kDQogICAgICAgIGVsZWN0cm9uIGRlbnNpdHkuDQoyMy4gKiphcHB0YWluZXIvMS4xLjYqKg0KICAgIC0gICAqKkFwcHRhaW5lciAoZm9ybWVybHkgU2luZ3VsYXJpdHkpKio6IEEgY29udGFpbmVyIHBsYXRmb3JtDQogICAgICAgIGRlc2lnbmVkIGZvciBIUEMgZW52aXJvbm1lbnRzLCBhbGxvd2luZyBwb3J0YWJsZSBhbmQNCiAgICAgICAgcmVwcm9kdWNpYmxlIHNvZnR3YXJlIGVudmlyb25tZW50cy4NCjI0LiAqKmJsZW5kZXIvNC4wLjIqKg0KICAgIC0gICAqKkJsZW5kZXIqKjogT3Blbi1zb3VyY2UgM0QgbW9kZWxpbmcsIGFuaW1hdGlvbiwgYW5kIHJlbmRlcmluZw0KICAgICAgICBzb2Z0d2FyZS4NCjI1LiAqKmNmb3VyKioNCiAgICAtICAgKipWZXJzaW9uczogMi4xL21waSwgMi4xL25vbXBpIChEKSoqDQogICAgLSAgICoqQ0ZPVVIqKjogQSBxdWFudHVtIGNoZW1pc3RyeSBwcm9ncmFtIHBhY2thZ2UgZm9yIGhpZ2gtYWNjdXJhY3kNCiAgICAgICAgY2FsY3VsYXRpb25zLg0KMjYuICoqY3J5c3RhbCoqDQogICAgLSAgICoqVmVyc2lvbnM6IDIzLzEuMC4xLCAyMy8xLjAuMS0xIChEKSoqDQogICAgLSAgICoqQ1JZU1RBTCoqOiBTb2Z0d2FyZSBmb3IgY29tcHV0aW5nIHRoZSBlbGVjdHJvbmljIHN0cnVjdHVyZSBvZg0KICAgICAgICBwZXJpb2RpYyBzeXN0ZW1zLg0KMjcuICoqZGVtb24vNi4yLjIqKg0KICAgIC0gICAqKmRlTW9uKio6IEEgc29mdHdhcmUgZm9yIGRlbnNpdHkgZnVuY3Rpb25hbCB0aGVvcnkgKERGVCkNCiAgICAgICAgY2FsY3VsYXRpb25zLg0KMjguICoqZHM5LzguNC4xKioNCiAgICAtICAgKipEUzkqKjogQSB0b29sIGZvciBhc3Ryb25vbWljYWwgZGF0YSB2aXN1YWxpemF0aW9uIGFuZA0KICAgICAgICBhbmFseXNpcy4NCjI5LiAqKmdhbWVzcy8yMDIyLjIqKg0KICAgIC0gICAqKkdBTUVTUyAoR2VuZXJhbCBBdG9taWMgYW5kIE1vbGVjdWxhciBFbGVjdHJvbmljIFN0cnVjdHVyZQ0KICAgICAgICBTeXN0ZW0pKio6IEEgcHJvZ3JhbSBmb3IgY29tcHV0YXRpb25hbCBjaGVtaXN0cnkuDQozMC4gKipndW9yaSoqDQogICAgLSAgICoqVmVyc2lvbnM6IDEwLjAuMSwgMTEuMC4xIChEKSoqDQogICAgLSAgICoqR3Vyb2JpKio6IEFuIG9wdGltaXphdGlvbiBzb2x2ZXIgZm9yIG1hdGhlbWF0aWNhbCBwcm9ncmFtbWluZy4NCjMxLiAqKmlkbC84LjAqKg0KICAgIC0gICAqKklETCAoSW50ZXJhY3RpdmUgRGF0YSBMYW5ndWFnZSkqKjogQSBwcm9ncmFtbWluZyBsYW5ndWFnZSB1c2VkDQogICAgICAgIGZvciBkYXRhIGFuYWx5c2lzLCB2aXN1YWxpemF0aW9uLCBhbmQgY3Jvc3MtcGxhdGZvcm0gYXBwbGljYXRpb24NCiAgICAgICAgZGV2ZWxvcG1lbnQuDQozMi4gKiptb2xkZW4vNi45KioNCiAgICAtICAgKipNb2xkZW4qKjogQSB2aXN1YWxpemF0aW9uIHBhY2thZ2UgZm9yIG1vbGVjdWxhciBhbmQgZWxlY3Ryb25pYw0KICAgICAgICBzdHJ1Y3R1cmUuDQozMy4gKiptb2xwcm8qKg0KICAgIC0gICAqKlZlcnNpb25zOiAyMDIyLjMsIDIwMjQuMSAoRCkqKg0KICAgIC0gICAqKk1vbHBybyoqOiBTb2Z0d2FyZSBmb3IgaGlnaC1hY2N1cmFjeSBlbGVjdHJvbmljIHN0cnVjdHVyZQ0KICAgICAgICBjYWxjdWxhdGlvbnMuDQozNC4gKiptb2pvLzAuNi4xKioNCiAgICAtICAgKipNb2pvKio6IEEgZnJhbWV3b3JrIGZvciBjcmVhdGluZyBtYWNoaW5lIGxlYXJuaW5nIHBpcGVsaW5lcy4NCjM1LiAqKnF1YW50dW1fYXRrKioNCiAgICAtICAgKipWZXJzaW9uczogMjAyMi4wMy1TUDEsIDIwMjIuMTItU1AxIChEKSoqDQogICAgLSAgICoqUXVhbnR1bUFUSyoqOiBBIHNvZnR3YXJlIHBsYXRmb3JtIGZvciBhdG9taWMtc2NhbGUgbW9kZWxpbmcNCiAgICAgICAgYW5kIHNpbXVsYXRpb24uDQozNi4gKipzYXMvOS40bTcqKg0KICAgIC0gICAqKlNBUyoqOiBBIHNvZnR3YXJlIHN1aXRlIGZvciBhZHZhbmNlZCBhbmFseXRpY3MsIGJ1c2luZXNzDQogICAgICAgIGludGVsbGlnZW5jZSwgZGF0YSBtYW5hZ2VtZW50LCBhbmQgcHJlZGljdGl2ZSBhbmFseXRpY3MuDQozNy4gKipzeW5vcHN5cy9waG90b25pY3NvbHV0aW9ucy8yMDIxLjA5LTMqKg0KICAgIC0gICAqKlN5bm9wc3lzIFBob3RvbmljIFNvbHV0aW9ucyoqOiBTb2Z0d2FyZSB0b29scyBmb3IgZGVzaWduaW5nDQogICAgICAgIGFuZCBzaW11bGF0aW5nIHBob3RvbmljIGFuZCBvcHRvZWxlY3Ryb25pYyBjb21wb25lbnRzIGFuZA0KICAgICAgICBzeXN0ZW1zLg0KMzguICoqdGNhZC8yMDIyKioNCiAgICAtICAgKipUQ0FEKio6IFRlY2hub2xvZ3kgQ29tcHV0ZXItQWlkZWQgRGVzaWduIHNvZnR3YXJlIHVzZWQgZm9yDQogICAgICAgIHNpbXVsYXRpbmcgc2VtaWNvbmR1Y3RvciBwcm9jZXNzaW5nIGFuZCBkZXZpY2VzLg0KMzkuICoqdGV4bGl2ZS8yMDIzKioNCiAgICAtICAgKipUZVggTGl2ZSoqOiBBIGNvbXByZWhlbnNpdmUgVGVYIHN5c3RlbSBmb3IgZWRpdGluZyBhbmQNCiAgICAgICAgcHVibGlzaGluZyBkb2N1bWVudHMuDQo0MC4gKip2YXNwLzUuNC40KioNCiAgICAtICAgKipWQVNQIChWaWVubmEgQWIgaW5pdGlvIFNpbXVsYXRpb24gUGFja2FnZSkqKjogQSBzb2Z0d2FyZSBmb3INCiAgICAgICAgYXRvbWljLXNjYWxlIG1hdGVyaWFscyBtb2RlbGluZy4NCg0KIyMjIENvbXBpbGVyIE1vZHVsZXMNCg0KMS4gICoqYW1kLzQuMC4wKioNCiAgICAtICAgKipBTUQgT3B0aW1pemluZyBDL0MrKyBDb21waWxlcioqOiBPcHRpbWl6ZWQgZm9yIEFNRCBwcm9jZXNzb3JzLA0KICAgICAgICBzdXBwb3J0aW5nIEMsIEMrKywgYW5kIEZvcnRyYW4gcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLg0KMi4gICoqZ2NjKioNCiAgICAtICAgKipWZXJzaW9uczogNi4zLjAsIDExLjIuMCAoRCkqKg0KICAgIC0gICAqKkdDQyAoR05VIENvbXBpbGVyIENvbGxlY3Rpb24pKio6IEEgd2lkZWx5LXVzZWQgY29tcGlsZXINCiAgICAgICAgc3VwcG9ydGluZyBDLCBDKyssIGFuZCBGb3J0cmFuLg0KMy4gICoqaW50ZWwqKg0KICAgIC0gICAqKlZlcnNpb25zOiAyMDIzLjEsIG9uZWFwaS8yMDIzLjIqKg0KICAgIC0gICAqKkludGVsIENvbXBpbGVycyoqOiBPcHRpbWl6ZWQgZm9yIEludGVsIHByb2Nlc3NvcnMsIHN1cHBvcnRpbmcNCiAgICAgICAgaGlnaC1wZXJmb3JtYW5jZSBjb21wdXRpbmcgYW5kIGFwcGxpY2F0aW9ucy4NCjQuICAqKm52aWRpYSoqDQogICAgLSAgICoqVmVyc2lvbnM6IDIxLjMsIDIzLjUgKEQpKioNCiAgICAtICAgKipOVklESUEgSFBDIFNESyoqOiBDb21waWxlcnMgYW5kIHRvb2xzIGZvciBHUFUtYWNjZWxlcmF0ZWQNCiAgICAgICAgYXBwbGljYXRpb25zLCBzdXBwb3J0aW5nIENVREEgYW5kIG90aGVyIHBhcmFsbGVsIHByb2dyYW1taW5nDQogICAgICAgIG1vZGVscy4NCjUuICAqKnJ1c3QvMS43MS4wKioNCiAgICAtICAgKipSdXN0Kio6IEEgc3lzdGVtcyBwcm9ncmFtbWluZyBsYW5ndWFnZSBmb2N1c2VkIG9uIHNhZmV0eSwNCiAgICAgICAgc3BlZWQsIGFuZCBjb25jdXJyZW5jeS4NCg0KIyMjIFVzYWdlIEV4YW1wbGUNCg0KVG8gdXNlIHRoZXNlIG1vZHVsZXMgb24gdGhlIEhQQyBzeXN0ZW0sIHlvdSBuZWVkIHRvIGxvYWQgdGhlIHJlcXVpcmVkDQptb2R1bGVzIGJlZm9yZSBjb21waWxpbmcgb3IgcnVubmluZyBteSBjb2RlLiBIZXJl4oCZcyBhbiBleGFtcGxlIG9mIGhvdyB0bw0KbG9hZCBhIG1vZHVsZSBhbmQgY29tcGlsZSBhIHByb2dyYW06DQoNCmBgYCBzaA0KIyBMb2FkIHRoZSBHQ0MgY29tcGlsZXIgbW9kdWxlDQptb2R1bGUgbG9hZCBnY2MvMTEuMi4wDQoNCiMgQ29tcGlsZSBhIEMgcHJvZ3JhbQ0KZ2NjIC1vIG15X3Byb2dyYW0gbXlfcHJvZ3JhbS5jDQoNCiMgTG9hZCB0aGUgUHl0aG9uIGVudmlyb25tZW50IHdpdGggQ29uZGENCm1vZHVsZSBsb2FkIGNvbmRhDQpjb25kYSBhY3RpdmF0ZSBteV9lbnYNCg0KIyBSdW4gYSBQeXRob24gc2NyaXB0DQpweXRob24gbXlfc2NyaXB0LnB5DQpgYGANCg0KIyBwcm9qZWN0IG9uIG0zDQoNClRvIGRldmVsb3AgYSBRdWFudHVtIEdlbmVyYXRpdmUgQWR2ZXJzYXJpYWwgTmV0d29yayAoUUdBTikgb24gdGhlIE0zIEhQQw0Kc3lzdGVtIGZvciBteSBwcm9qZWN0LCB5b3Ugd2lsbCBuZWVkIHRvIGludGVncmF0ZSBxdWFudHVtIGNvbXB1dGluZyB3aXRoDQpjbGFzc2ljYWwgY29tcHV0aW5nIHRvb2xzLiBIZXJl4oCZcyBhIGNvbXByZWhlbnNpdmUgc3RlcC1ieS1zdGVwIGd1aWRlIHRvDQpoZWxwIHlvdSBnZXQgc3RhcnRlZDoNCg0KIyMjIFN0ZXAgMTogU2V0IFVwIG15IEVudmlyb25tZW50DQoNCiMjIyMgMS4gQ29ubmVjdCB0byB0aGUgTTMgSFBDIFN5c3RlbQ0KDQpVc2UgU1NIIHRvIGNvbm5lY3QgdG8gdGhlIE0zIEhQQyBzeXN0ZW0uDQoNCmBgYCBzaA0Kc3NoIGptY3BoYXVsQG0zbG9naW4wMQ0KYGBgDQoNCiMjIyMgMi4gTG9hZCBOZWNlc3NhcnkgTW9kdWxlcw0KDQpGaXJzdCwgeW91IG5lZWQgdG8gbG9hZCB0aGUgbmVjZXNzYXJ5IG1vZHVsZXMgZm9yIG15IGVudmlyb25tZW50LiBUaGlzDQppbmNsdWRlcyBjb21waWxlcnMsIFB5dGhvbiBlbnZpcm9ubWVudHMsIGFuZCBhbnkgcmVsZXZhbnQgYXBwbGljYXRpb25zLg0KDQpgYGAgc2gNCm1vZHVsZSBsb2FkIGdjYy8xMS4yLjANCm1vZHVsZSBsb2FkIGNvbmRhDQptb2R1bGUgbG9hZCBudmlkaWEvMjMuNQ0KYGBgDQoNCiMjIyBTdGVwIDI6IENyZWF0ZSBhbmQgQWN0aXZhdGUgYSBDb25kYSBFbnZpcm9ubWVudA0KDQpVc2UgQ29uZGEgdG8gY3JlYXRlIGFuIGlzb2xhdGVkIGVudmlyb25tZW50IGZvciBteSBwcm9qZWN0LCB3aGljaCB3aWxsDQppbmNsdWRlIHRoZSBuZWNlc3NhcnkgUHl0aG9uIHBhY2thZ2VzIGZvciBib3RoIGNsYXNzaWNhbCBhbmQgcXVhbnR1bQ0KY29tcHV0aW5nLg0KDQpgYGAgc2gNCmNvbmRhIGNyZWF0ZSAtbiBxZ2FuX2VudiBweXRob249My45DQpjb25kYSBhY3RpdmF0ZSBxZ2FuX2Vudg0KYGBgDQoNCiMjIyBTdGVwIDM6IEluc3RhbGwgUmVxdWlyZWQgUHl0aG9uIFBhY2thZ2VzDQoNCldpdGggbXkgQ29uZGEgZW52aXJvbm1lbnQgYWN0aXZhdGVkLCBpbnN0YWxsIHRoZSBuZWNlc3NhcnkgUHl0aG9uDQpwYWNrYWdlcyBmb3IgcXVhbnR1bSBjb21wdXRpbmcsIG1hY2hpbmUgbGVhcm5pbmcsIGFuZCBHSVMgYXBwbGljYXRpb25zLg0KDQpgYGAgc2gNCiMgSW5zdGFsbCBiYXNpYyBwYWNrYWdlcyBmb3IgR0lTIGFuZCBxdWFudHVtIGNvbXB1dGluZw0KY29uZGEgaW5zdGFsbCBudW1weSBwYW5kYXMgZ2VvcGFuZGFzIGdkYWwgbWF0cGxvdGxpYg0KcGlwIGluc3RhbGwgcWlza2l0IHRlbnNvcmZsb3cgdG9yY2ggdG9yY2h2aXNpb24NCmBgYA0KDQojIyMgU3RlcCA0OiBEZXZlbG9wIHRoZSBRR0FODQoNCllvdSB3aWxsIGNyZWF0ZSB0aGUgUUdBTiBieSBpbnRlZ3JhdGluZyBib3RoIGNsYXNzaWNhbCBhbmQgcXVhbnR1bQ0KY29tcG9uZW50cy4gQmVsb3cgaXMgYW4gZXhhbXBsZSBzZXR1cC4NCg0KIyMjIyBDbGFzc2ljYWwgR0FOIGZvciBHSVMgRGF0YSAodXNpbmcgUHlUb3JjaCkNCg0KQ3JlYXRlIGFuZCB0cmFpbiBhIGNsYXNzaWNhbCBHQU4gZm9yIGdlbmVyYXRpbmcgYW5kIGFuYWx5emluZyBHSVMgZGF0YS4NCg0KKipHQU4gQ29tcG9uZW50cyoqDQoNCmBgYCBweXRob24NCmltcG9ydCB0b3JjaA0KaW1wb3J0IHRvcmNoLm5uIGFzIG5uDQppbXBvcnQgdG9yY2gub3B0aW0gYXMgb3B0aW0NCg0KY2xhc3MgR2VuZXJhdG9yKG5uLk1vZHVsZSk6DQogICAgZGVmIF9faW5pdF9fKHNlbGYpOg0KICAgICAgICBzdXBlcihHZW5lcmF0b3IsIHNlbGYpLl9faW5pdF9fKCkNCiAgICAgICAgc2VsZi5tYWluID0gbm4uU2VxdWVudGlhbCgNCiAgICAgICAgICAgIG5uLkxpbmVhcigxMDAsIDI1NiksDQogICAgICAgICAgICBubi5SZUxVKCksDQogICAgICAgICAgICBubi5MaW5lYXIoMjU2LCA1MTIpLA0KICAgICAgICAgICAgbm4uUmVMVSgpLA0KICAgICAgICAgICAgbm4uTGluZWFyKDUxMiwgMTAyNCksDQogICAgICAgICAgICBubi5UYW5oKCkNCiAgICAgICAgKQ0KICAgIA0KICAgIGRlZiBmb3J3YXJkKHNlbGYsIGlucHV0KToNCiAgICAgICAgcmV0dXJuIHNlbGYubWFpbihpbnB1dCkNCg0KY2xhc3MgRGlzY3JpbWluYXRvcihubi5Nb2R1bGUpOg0KICAgIGRlZiBfX2luaXRfXyhzZWxmKToNCiAgICAgICAgc3VwZXIoRGlzY3JpbWluYXRvciwgc2VsZikuX19pbml0X18oKQ0KICAgICAgICBzZWxmLm1haW4gPSBubi5TZXF1ZW50aWFsKA0KICAgICAgICAgICAgbm4uTGluZWFyKDEwMjQsIDUxMiksDQogICAgICAgICAgICBubi5MZWFreVJlTFUoMC4yKSwNCiAgICAgICAgICAgIG5uLkxpbmVhcig1MTIsIDI1NiksDQogICAgICAgICAgICBubi5MZWFreVJlTFUoMC4yKSwNCiAgICAgICAgICAgIG5uLkxpbmVhcigyNTYsIDEpLA0KICAgICAgICAgICAgbm4uU2lnbW9pZCgpDQogICAgICAgICkNCiAgICANCiAgICBkZWYgZm9yd2FyZChzZWxmLCBpbnB1dCk6DQogICAgICAgIHJldHVybiBzZWxmLm1haW4oaW5wdXQpDQoNCiMgSW5pdGlhbGl6ZSBtb2RlbHMNCkcgPSBHZW5lcmF0b3IoKQ0KRCA9IERpc2NyaW1pbmF0b3IoKQ0KDQojIE9wdGltaXplcnMgYW5kIGxvc3MgZnVuY3Rpb24NCm9wdGltaXplckcgPSBvcHRpbS5BZGFtKEcucGFyYW1ldGVycygpLCBscj0wLjAwMDIpDQpvcHRpbWl6ZXJEID0gb3B0aW0uQWRhbShELnBhcmFtZXRlcnMoKSwgbHI9MC4wMDAyKQ0KY3JpdGVyaW9uID0gbm4uQkNFTG9zcygpDQoNCiMgRHVtbXkgdHJhaW5pbmcgbG9vcCBwbGFjZWhvbGRlcg0KZXBvY2hzID0gMTAwDQpmb3IgZXBvY2ggaW4gcmFuZ2UoZXBvY2hzKToNCiAgICAjIFRyYWluaW5nIGNvZGUgaGVyZQ0KICAgIHBhc3MNCmBgYA0KDQojIyMjIEludGVncmF0ZSBRdWFudHVtIENvbXBvbmVudCB1c2luZyBRaXNraXQNCg0KQWRkIHRoZSBxdWFudHVtIGNvbXBvbmVudCB0byBteSBHQU4gdXNpbmcgUWlza2l0IGZvciBxdWFudHVtIGRhdGENCmdlbmVyYXRpb24uDQoNCmBgYCBweXRob24NCmZyb20gcWlza2l0IGltcG9ydCBRdWFudHVtQ2lyY3VpdCwgQWVyLCBleGVjdXRlDQpmcm9tIHFpc2tpdC52aXN1YWxpemF0aW9uIGltcG9ydCBwbG90X2hpc3RvZ3JhbQ0KDQpkZWYgcXVhbnR1bV9nZW5lcmF0b3IoKToNCiAgICBxYyA9IFF1YW50dW1DaXJjdWl0KDEsIDEpDQogICAgcWMuaCgwKQ0KICAgIHFjLm1lYXN1cmUoMCwgMCkNCiAgICByZXR1cm4gcWMNCg0KYmFja2VuZCA9IEFlci5nZXRfYmFja2VuZCgncWFzbV9zaW11bGF0b3InKQ0KcWMgPSBxdWFudHVtX2dlbmVyYXRvcigpDQpqb2IgPSBleGVjdXRlKHFjLCBiYWNrZW5kLCBzaG90cz0xMDAwKQ0KcmVzdWx0ID0gam9iLnJlc3VsdCgpDQpjb3VudHMgPSByZXN1bHQuZ2V0X2NvdW50cyhxYykNCnByaW50KGNvdW50cykNCg0KcGxvdF9oaXN0b2dyYW0oY291bnRzKQ0KYGBgDQoNCiMjIyBTdGVwIDU6IEludGVncmF0ZSBDbGFzc2ljYWwgYW5kIFF1YW50dW0gQ29tcG9uZW50cyBpbiB0aGUgUUdBTg0KDQpDb21iaW5lIHF1YW50dW0tZ2VuZXJhdGVkIGRhdGEgd2l0aCBjbGFzc2ljYWwgR0FOIHRyYWluaW5nOg0KDQpgYGAgcHl0aG9uDQppbXBvcnQgZ2VvcGFuZGFzIGFzIGdwZA0KaW1wb3J0IG51bXB5IGFzIG5wDQoNCiMgRXhhbXBsZSBvZiBpbnRlZ3JhdGluZyBxdWFudHVtLWdlbmVyYXRlZCBkYXRhIHdpdGggY2xhc3NpY2FsIEdBTiB0cmFpbmluZw0KZm9yIGVwb2NoIGluIHJhbmdlKGVwb2Nocyk6DQogICAgZm9yIGksIGRhdGEgaW4gZW51bWVyYXRlKGRhdGFsb2FkZXIpOg0KICAgICAgICAjIFF1YW50dW0gZGF0YSBnZW5lcmF0aW9uDQogICAgICAgIHFjID0gcXVhbnR1bV9nZW5lcmF0b3IoKQ0KICAgICAgICBqb2IgPSBleGVjdXRlKHFjLCBiYWNrZW5kLCBzaG90cz0xKQ0KICAgICAgICByZXN1bHQgPSBqb2IucmVzdWx0KCkNCiAgICAgICAgcXVhbnR1bV9kYXRhID0gbGlzdChyZXN1bHQuZ2V0X2NvdW50cyhxYykua2V5cygpKVswXQ0KDQogICAgICAgICMgQ29udmVydCBxdWFudHVtIGRhdGEgdG8gaW5wdXQgZm9ybWF0DQogICAgICAgIHF1YW50dW1faW5wdXQgPSB0b3JjaC5UZW5zb3IoW2Zsb2F0KGJpdCkgZm9yIGJpdCBpbiBxdWFudHVtX2RhdGFdKQ0KDQogICAgICAgICMgQ2xhc3NpY2FsIEdBTiB0cmFpbmluZyBzdGVwcw0KICAgICAgICByZWFsX2RhdGEgPSBkYXRhWzBdDQogICAgICAgIGZha2VfZGF0YSA9IEcocXVhbnR1bV9pbnB1dCkNCg0KICAgICAgICAjIFVwZGF0ZSBEaXNjcmltaW5hdG9yDQogICAgICAgIEQuemVyb19ncmFkKCkNCiAgICAgICAgcmVhbF9vdXRwdXQgPSBEKHJlYWxfZGF0YSkNCiAgICAgICAgZmFrZV9vdXRwdXQgPSBEKGZha2VfZGF0YS5kZXRhY2goKSkNCiAgICAgICAgbG9zc0QgPSBjcml0ZXJpb24ocmVhbF9vdXRwdXQsIHRvcmNoLm9uZXNfbGlrZShyZWFsX291dHB1dCkpICsgY3JpdGVyaW9uKGZha2Vfb3V0cHV0LCB0b3JjaC56ZXJvc19saWtlKGZha2Vfb3V0cHV0KSkNCiAgICAgICAgbG9zc0QuYmFja3dhcmQoKQ0KICAgICAgICBvcHRpbWl6ZXJELnN0ZXAoKQ0KDQogICAgICAgICMgVXBkYXRlIEdlbmVyYXRvcg0KICAgICAgICBHLnplcm9fZ3JhZCgpDQogICAgICAgIGZha2Vfb3V0cHV0ID0gRChmYWtlX2RhdGEpDQogICAgICAgIGxvc3NHID0gY3JpdGVyaW9uKGZha2Vfb3V0cHV0LCB0b3JjaC5vbmVzX2xpa2UoZmFrZV9vdXRwdXQpKQ0KICAgICAgICBsb3NzRy5iYWNrd2FyZCgpDQogICAgICAgIG9wdGltaXplckcuc3RlcCgpDQoNCiAgICAgICAgIyBQcmludCBsb3NzZXMgKG9wdGlvbmFsKQ0KICAgICAgICBwcmludChmJ0Vwb2NoIFt7ZXBvY2h9L3tlcG9jaHN9XSwgU3RlcCBbe2l9L3tsZW4oZGF0YWxvYWRlcil9XSwgTG9zcyBEOiB7bG9zc0QuaXRlbSgpfSwgTG9zcyBHOiB7bG9zc0cuaXRlbSgpfScpDQpgYGANCg0KIyMjIFN0ZXAgNjogUHJlcGFyZSBHSVMgRGF0YSBmb3IgVHJhaW5pbmcNCg0KTG9hZCBhbmQgcHJlcHJvY2VzcyBHSVMgZGF0YSBmb3IgdXNlIGluIHRoZSBRR0FOLg0KDQpgYGAgcHl0aG9uDQojIExvYWQgR0lTIGRhdGEgdXNpbmcgR2VvUGFuZGFzDQpnaXNfZGF0YSA9IGdwZC5yZWFkX2ZpbGUoJ3BhdGhfdG9fbXlfZ2lzX2RhdGEuc2hwJykNCg0KIyBQcmVwcm9jZXNzIEdJUyBkYXRhDQojIEV4YW1wbGU6IG5vcm1hbGl6ZSBkYXRhLCBjb252ZXJ0IHRvIHRlbnNvciwgZXRjLg0KZ2lzX2RhdGEgPSBnaXNfZGF0YS50b19jcnMoZXBzZz00MzI2KSAgIyBDb252ZXJ0IHRvIGEgY29tbW9uIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbQ0KZGF0YV90ZW5zb3IgPSB0b3JjaC50ZW5zb3IoZ2lzX2RhdGEudmFsdWVzLCBkdHlwZT10b3JjaC5mbG9hdDMyKQ0KYGBgDQoNCiMjIyBTdGVwIDc6IFRyYWluIGFuZCBFdmFsdWF0ZSB0aGUgUUdBTg0KDQpSdW4gdGhlIHRyYWluaW5nIHByb2Nlc3MgYW5kIGV2YWx1YXRlIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlLg0KDQpgYGAgcHl0aG9uDQojIFRyYWluaW5nIGFuZCBldmFsdWF0aW9uIGxvb3ANCmZvciBlcG9jaCBpbiByYW5nZShlcG9jaHMpOg0KICAgIGZvciBpLCBkYXRhIGluIGVudW1lcmF0ZShkYXRhbG9hZGVyKToNCiAgICAgICAgIyBRdWFudHVtIGFuZCBjbGFzc2ljYWwgZGF0YSBpbnRlZ3JhdGlvbiBhbmQgdHJhaW5pbmcNCiAgICAgICAgIyBteSB0cmFpbmluZyBjb2RlIGhlcmUuLi4NCg0KIyBTYXZlIHRoZSBtb2RlbA0KdG9yY2guc2F2ZShHLnN0YXRlX2RpY3QoKSwgJ2dlbmVyYXRvcl9tb2RlbC5wdGgnKQ0KdG9yY2guc2F2ZShELnN0YXRlX2RpY3QoKSwgJ2Rpc2NyaW1pbmF0b3JfbW9kZWwucHRoJykNCg0KIyBFdmFsdWF0ZSB0aGUgbW9kZWwNCiMgbXkgZXZhbHVhdGlvbiBjb2RlIGhlcmUuLi4NCmBgYA0KDQojIyMgU3RlcCA4OiBSdW4gb24gdGhlIEhQQyBTeXN0ZW0NCg0KU3VibWl0am9iIHRvIHRoZSBIUEMgc3lzdGVtIHVzaW5nIGEgam9iIHNjaGVkdWxlciBsaWtlIFNMVVJNIG9yIFBCUy4NCkhlcmUgaXMgYW4gZXhhbXBsZSBTTFVSTSBzY3JpcHQ6DQoNCmBgYCBzaA0KIyEvYmluL2Jhc2gNCiNTQkFUQ0ggLS1qb2ItbmFtZT1xZ2FuX2dpcw0KI1NCQVRDSCAtLW91dHB1dD1xZ2FuX2dpc19vdXRwdXQudHh0DQojU0JBVENIIC0tbnRhc2tzPTENCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTQNCiNTQkFUQ0ggLS1tZW09MTZHDQojU0JBVENIIC0tdGltZT0yNDowMDowMA0KI1NCQVRDSCAtLXBhcnRpdGlvbj1ncHUNCiNTQkFUQ0ggLS1ncHVzPTENCg0KbW9kdWxlIGxvYWQgZ2NjLzExLjIuMA0KbW9kdWxlIGxvYWQgY29uZGENCm1vZHVsZSBsb2FkIG52aWRpYS8yMy41DQoNCmNvbmRhIGFjdGl2YXRlIHFnYW5fZW52DQoNCnB5dGhvbiBteV9xZ2FuX3NjcmlwdC5weQ0KYGBgDQoNClNhdmUgdGhpcyBzY3JpcHQgYXMgYHN1Ym1pdF9qb2Iuc2hgIGFuZCBzdWJtaXQgaXQgdG8gdGhlIGpvYiBzY2hlZHVsZXI6DQoNCmBgYCBzaA0Kc2JhdGNoIHN1Ym1pdF9qb2Iuc2gNCmBgYA0KDQpbSFBDDQpjb25kYV0oaHR0cHM6Ly9zb3V0aGVybm1ldGhvZGlzdHVuaXZlcnNpdHkuZ2l0aHViLmlvL1NNVV9TdXBlclBPRF8xMDEvMDItV29ya2luZyUyMHdpdGglMjBDb25kYS9pbmRleC5odG1sKQ0KDQoNCiMjDQoNCm5hbWU6IGVzc2VudGlhbF9lbnYNCmNoYW5uZWxzOg0KICAtIGRlZmF1bHRzDQogIC0gY29uZGEtZm9yZ2UNCmRlcGVuZGVuY2llczoNCiAgLSBweXRob249My4xMA0KICAtIG51bXB5DQogIC0gcGFuZGFzDQogIC0gbWF0cGxvdGxpYg0KICAtIHNjaXB5DQogIC0gc2Npa2l0LWxlYXJuDQogIC0ganVweXRlcg0KICAtIHBpcA0KICAtIHBpcDoNCiAgICAgIC0gcWlza2l0DQogICAgICAtIHB5dG9yY2gNCiAgICAgIC0gdG9yY2h2aXNpb24NCiAgICAgIC0gdGVuc29yZmxvdw0KICAgICAgLSBrZXJhcw0KICAgICAgLSBwbG90bHkNCiAgICAgIC0gc2VhYm9ybg0KDQoNCmNvbmRhIGVudiBjcmVhdGUgLWYgZW52aXJvbm1lbnQzLnltbA0KDQojIyMgcGlwIHVuaW5zdGFsbCB1bHRyYWx5dGljcyBydXN0d29ya3ggbnVtcHkgICAgIHBpcCBpbnN0YWxsIHVsdHJhbHl0aWNzIHJ1c3R3b3JreD09MC4xNC4yDQoNCg0KIyMjIGNvbmRhIGVudiBleHBvcnQgPiBlbnZpcm9ubWVudC55bWwNCiMjIyBjb25kYSB1cGRhdGUgLS1hbGwgLS1kcnktcnVuDQoNCmBgYHtweXRob259DQpjb25kYSBjcmVhdGUgLW4gbmV3X2Vudl9uYW1lIHB5dGhvbj0zLjEwDQpjb25kYSBhY3RpdmF0ZSBuZXdfZW52X25hbWUNCmNvbmRhIGluc3RhbGwgbnVtcHkgc2NpcHkgbWF0cGxvdGxpYiBzZWFib3JuIC4uLg0KYGBgDQoNCmBgYHtzaH0NCmNvbmRhIGVudiBleHBvcnQgPiBlbnZpcm9ubWVudF9uYW1lLnltbA0KYGBgDQoNCg0KYGBge3lhbWx9DQpkZXBlbmRlbmNpZXM6DQogIC0gbnVtcHk9MS4yNi40DQogIC0gc2NpcHk9MS4xNC4wDQogIC0gbWF0cGxvdGxpYj0zLjguNA0KICAtIHNlYWJvcm49MC4xMy4yDQogIC0gLi4uDQpgYGANCg0KYGB7c2h9DQpjb25kYSBlbnYgY3JlYXRlIC1mIGVudmlyb25tZW50X25hbWUueW1sDQpgYGANCg0KYGBge3NofQ0KY29uZGEgY29uZmlnIC0tYWRkIGNoYW5uZWxzIGNvbmRhLWZvcmdlDQpjb25kYSBjb25maWcgLS1zZXQgY2hhbm5lbF9wcmlvcml0eSBzdHJpY3QNCmBgYA0KDQoNCiMjIG5hbm8gZW52aXJvbm1lbnQueW1sIC0gc2F2ZSBhcyBuZXcgIA0KDQpgYGB7eWFtbH0NCm5hbWU6IG15X25ld19lbnYNCmNoYW5uZWxzOg0KICAtIGNvbmRhLWZvcmdlDQogIC0gZGVmYXVsdHMNCmRlcGVuZGVuY2llczoNCiAgLSBweXRob249My4xMA0KICAtIG51bXB5PTEuMjYuNA0KICAtIHNjaXB5PTEuMTQuMA0KICAtIG1hdHBsb3RsaWI9My44LjQNCiAgLSBzZWFib3JuPTAuMTMuMg0KICAtIHBhbmRhcz0yLjIuMg0KICAtIHRxZG09NC42Ni40DQogIC0ganVweXRlcg0KICAtIGlweWtlcm5lbA0KICAtIHNjaWtpdC1sZWFybj0xLjUuMA0KICAtIHB5dG9yY2g9Mi4zLjENCiAgLSB0b3JjaHZpc2lvbj0wLjE4LjENCiAgLSBxaXNraXQ9MS4xLjENCiAgLSBweXVuaWNvcm4NCiAgLSB0ZW5zb3JmbG93DQogIC0ga2VyYXMNCiAgLSBwbG90bHkNCiAgLSBoNXB5DQogIC0gaDVuZXRjZGYNCiAgLSBmb2xpdW0NCiAgLSBjYXJ0b3B5DQogIC0gcHktb3BlbmN2DQogIC0gb3BlbmN2DQpgYGANCg0KDQojIyNjb25kYSBlbnYgZXhwb3J0ID4gZW52aXJvbm1lbnRfbmFtZS55bWwNCg0KIyMjIG1vZHVsZSBsb2FkIGdjYy8xMS4yLjANCiMjIyBtb2R1bGUgbG9hZCBjb25kYQ0KDQoNCmBgYHtzc2h9DQpzYWxsb2MgLS1ub2Rlcz0xIC0tbnRhc2tzPTEgLS1jcHVzLXBlci10YXNrPTQgLS1tZW09MTZHIC0tdGltZT0wMjowMDowMCAtLXBhcnRpdGlvbj1iYXRjaCAtLWdyZXM9Z3B1OjENCg0KDQoNCm1vZHVsZSBsb2FkIGdjYy8xMS4yLjANCm1vZHVsZSBsb2FkIGNvbmRhDQpjb25kYSBhY3RpdmF0ZSBxZ2FuX2Vudg0KDQpGdWxsIENvbW1hbmQgU2VxdWVuY2UNCkFsbG9jYXRlIHJlc291cmNlcyBvbiB0aGUgSFBDIHN5c3RlbToNCg0Kc2gNCkNvcHkgY29kZQ0Kc2FsbG9jIC0tbm9kZXM9MSAtLW50YXNrcz0xIC0tY3B1cy1wZXItdGFzaz00IC0tbWVtPTE2RyAtLXRpbWU9MDI6MDA6MDAgLS1wYXJ0aXRpb249YmF0Y2ggLS1ncmVzPWdwdToxDQpMb2FkIG5lY2Vzc2FyeSBtb2R1bGVzIGFuZCBhY3RpdmF0ZSBDb25kYSBlbnZpcm9ubWVudDoNCg0Kc2gNCkNvcHkgY29kZQ0KbW9kdWxlIGxvYWQgZ2NjLzExLjIuMA0KbW9kdWxlIGxvYWQgY29uZGENCmNvbmRhIGFjdGl2YXRlIHFnYW5fZW52DQpjb25kYSBpbnN0YWxsIGp1cHl0ZXINClN0YXJ0IEp1cHl0ZXIgTm90ZWJvb2s6DQoNCnNoDQpDb3B5IGNvZGUNCmp1cHl0ZXItbm90ZWJvb2sgLS1uby1icm93c2VyIC0tcG9ydD04ODg4DQpJZiB0aGlzIGZhaWxzLCB0cnkgSnVweXRlckxhYjoNCg0Kc2gNCkNvcHkgY29kZQ0KanVweXRlciBsYWIgLS1uby1icm93c2VyIC0tcG9ydD04ODg4DQpTZXQgdXAgU1NIIHR1bm5lbGluZyBvbiB5b3VyIGxvY2FsIG1hY2hpbmU6DQoNCnNoDQpDb3B5IGNvZGUNCnNzaCAtTiAtTCA4ODg4OmxvY2FsaG9zdDo4ODg4IGptY3BoYXVsQHNsb2dpbi0wMg0KQWNjZXNzIEp1cHl0ZXIgTm90ZWJvb2sgdmlhIHlvdXIgd2ViIGJyb3dzZXI6DQoNCnJ1YnkNCkNvcHkgY29kZQ0KaHR0cDovL2xvY2FsaG9zdDo4ODg4Lz90b2tlbj1ZT1VSX1RPS0VODQoNCg0KYGBg