Stream Name Texaschikkita Stream URL https://rpubs.com/Texaschikkita Stream ID 9962324179
Measurement Id G-CV2648GQMK
Study Guide: Recommender Systems Using Singular Value
Decomposition (SVD)
Key Concepts
- Recommender Systems:
- Systems designed to suggest items (e.g., movies) based on user
preferences or history.
- Example: Netflix suggesting movies based on user ratings and watch
history.
- Matrix Representation:
- Data is represented as a matrix:
- Rows: Users.
- Columns: Movies.
- Entries: Ratings (or 0 if no rating).
- Matrix Factorization:
- Decomposing the matrix into three smaller matrices:
- \(A = U \Sigma V^T\)
- \(U\): User latent factors (user
embedding).
- \(\Sigma\): Diagonal matrix with
singular values.
- \(V\): Movie latent factors (movie
embedding).
- Singular Value Decomposition (SVD):
- A mathematical method to decompose a matrix.
- Captures latent relationships between users and movies.
- Removes noise by considering top singular values (dimensionality
reduction).
Mathematical Representation
Given a matrix \(A\): 1.
Decomposition: \(A = U \Sigma
V^T\), where: - \(U\) and \(V\) are orthogonal matrices. - \(\Sigma\) is a diagonal matrix with singular
values in descending order.
- Interpretation:
- \(U\): User latent factors.
- \(V\): Movie latent factors.
- \(\Sigma\): Strength of latent
factors.
- Cosine Similarity: Used to measure similarity
between items: \[
\text{Cosine Similarity} = \frac{\mathbf{a} \cdot
\mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|}
\]
- Applied to columns of \(V\) to find
similar movies.
Implementation Steps
- Load Data:
- Extract user-movie ratings, movie details.
- Create Rating Matrix:
- Convert user-movie interactions into a matrix with rows as users and
columns as movies.
- Normalize Data:
- Normalize ratings for each movie to handle variations.
- Compute SVD:
- Use libraries (e.g., NumPy or SciPy) to decompose the matrix into
\(U, \Sigma,\) and \(V^T\).
- Find Recommendations:
- Use top singular values and corresponding vectors to:
- Recommend movies for a user.
- Find similar movies.
- Optimize with Reduced SVD:
- Use only top \(k\) singular values
(e.g., based on elbow point in singular value plot).
Visualization
- Matrix Decomposition:
- Original matrix \(A\) → \(U \Sigma V^T\).
- \(\Sigma\): Singular values plotted
to identify the elbow point (optimal \(k\)).
- Recommendation Example:
- Visualize top recommendations for a movie or user.
Code Example
import numpy as np
from numpy.linalg import svd
from sklearn.metrics.pairwise import cosine_similarity
# Step 1: Construct rating matrix
ratings_matrix = np.zeros((num_users, num_movies)) # Example: Fill with actual ratings data
# Step 2: Normalize ratings
mean_ratings = np.mean(ratings_matrix, axis=0)
normalized_matrix = ratings_matrix - mean_ratings
# Step 3: Compute SVD
U, S, VT = svd(normalized_matrix, full_matrices=False)
S_diag = np.diag(S)
# Step 4: Choose top k components (reduce noise)
k = 30 # Optimal number of singular values
U_k = U[:, :k]
S_k = S_diag[:k, :k]
VT_k = VT[:k, :]
# Step 5: Recommendations
def recommend_movies(movie_id, top_n=10):
movie_vector = VT_k[:, movie_id]
similarities = cosine_similarity(movie_vector.reshape(1, -1), VT_k.T)
top_indices = np.argsort(similarities[0])[-top_n:]
return top_indices[::-1]
# Example: Recommend movies similar to Toy Story (movie_id=1)
similar_movies = recommend_movies(movie_id=1)
print("Recommended Movies:", similar_movies)
Applications
- Movie Recommendations:
- Suggest movies for users (Netflix-style).
- Find similar movies (content-based).
- Advertising:
- Recommend ads to users with similar interests.
- Noise Reduction:
- Handle noisy data by focusing on top singular values.
Insights
- Advantages:
- Simple, interpretable, and effective for collaborative
filtering.
- Can handle sparse data.
- Limitations:
- Cold start problem: Cannot handle new users/movies without prior
data.
- No genre or metadata usage unless extended.
By understanding SVD, you can implement efficient recommender systems
and explore advanced techniques like principal component analysis or
neural network-based models for improved results.
Summary of Lecture on SVD for Recommender Systems
Key Points
- Introduction to SVD:
- Singular Value Decomposition (SVD) is used in recommendation systems
to decompose user-item matrices into three matrices \(U\), \(\Sigma\), and \(V^T\).
- This process provides vector representations (embeddings) of users
and items that capture latent relationships.
- Dataset Description:
- Input: User ratings of movies.
- Structure:
- Users (rows) × Movies (columns) matrix where entries represent
ratings.
- Ratings are normalized for consistency.
- Matrix Factorization:
- Matrix \(A\) (user-movie ratings)
is factorized as: \[
A = U \Sigma V^T
\]
- \(U\): Embedding for users.
- \(V\): Embedding for movies.
- \(\Sigma\): Diagonal matrix
containing singular values that represent the strength of corresponding
latent features.
- Dimensionality Reduction:
- Noise reduction is achieved by selecting top \(k\) singular values (\(\Sigma_k\)) and their corresponding
vectors.
- This approximates \(A\) as: \[
A_k \approx U_k \Sigma_k V_k^T
\]
- Applications:
- Recommendation: Find movies similar to a given
movie.
- Denoising: Reduce the effect of noisy, less
relevant features.
- Cold Start Problem: Address new user/movie
scenarios with default recommendations.
- Implementation:
- Use Python’s
numpy.linalg.svd
for SVD computation.
- Select the optimal \(k\) by
examining the singular values’ drop-off (elbow method).
Study Guide
Mathematical Background
- SVD Representation:
- Decompose a matrix \(A\): \[
A = U \Sigma V^T
\]
- Properties:
- \(U\) and \(V^T\) are orthogonal matrices.
- \(\Sigma\) contains singular values
sorted in descending order.
- Interpretation:
- \(U\): Represents user
embeddings.
- \(V^T\): Represents item
embeddings.
- \(\Sigma\): Importance of features
(higher values = more importance).
- Dimensionality Reduction:
- Select top \(k\) singular
values.
- Reduced matrix: \[
A_k = U_k \Sigma_k V_k^T
\]
Implementation Steps
Data Preparation:
- Construct a user-movie matrix with ratings.
- Normalize the ratings.
Compute SVD:
import numpy as np
# Compute SVD
U, S, Vt = np.linalg.svd(A, full_matrices=False)
# Select top k components
k = 30
U_k = U[:, :k]
S_k = np.diag(S[:k])
Vt_k = Vt[:k, :]
Cosine Similarity:
- Find similar movies using cosine similarity between movie vectors in
\(V_k\).
from sklearn.metrics.pairwise import cosine_similarity
# Compute cosine similarity
similarities = cosine_similarity(Vt_k.T)
# Recommend top n movies
movie_id = 0 # Movie of interest
top_n = 10
similar_movies = np.argsort(similarities[movie_id])[-top_n:]
Visualization
Singular Values (Scree Plot)
Plot the singular values to identify the elbow point where most
information is captured:
import matplotlib.pyplot as plt
plt.plot(S)
plt.xlabel('Component Index')
plt.ylabel('Singular Value')
plt.title('Scree Plot')
plt.show()
Recommendation Example
Heatmap of user-item interaction before and after dimensionality
reduction:
import seaborn as sns
sns.heatmap(A, cmap='coolwarm', cbar=True)
sns.heatmap(U_k @ S_k @ Vt_k, cmap='coolwarm', cbar=True)
Additional Tips
- Optimal \(k\):
- Use the “elbow method” or cross-validation on a subset of data.
- Cold Start Handling:
- Initialize new users/movies with averages or most popular
items.
- Comparison to PCA:
- Both PCA and SVD reduce dimensions by capturing maximum variance;
SVD is more general and directly applicable to user-item matrices.
- Extensions:
- Incorporate genres or user demographics for hybrid recommendation
systems.
Sample Code (Python)
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import matplotlib.pyplot as plt
# Sample user-item matrix
A = np.array([[5, 4, 0, 1],
[4, 0, 0, 1],
[1, 1, 0, 5],
[1, 0, 4, 4],
[0, 1, 5, 4]])
# Perform SVD
U, S, Vt = np.linalg.svd(A, full_matrices=False)
# Select top-k components
k = 2
U_k = U[:, :k]
S_k = np.diag(S[:k])
Vt_k = Vt[:k, :]
# Reconstruct reduced matrix
A_k = U_k @ S_k @ Vt_k
# Visualize original and reduced matrices
plt.subplot(1, 2, 1)
plt.title("Original Matrix")
sns.heatmap(A, annot=True, cmap='coolwarm')
plt.subplot(1, 2, 2)
plt.title("Reduced Matrix")
sns.heatmap(A_k, annot=True, cmap='coolwarm')
plt.show()
This code implements the concepts discussed and highlights the impact
of dimensionality reduction on recommendations.
Machine learning-based recommendation systems are powerful
engines using machine learning (ML) algorithms to segment customers
based on user data and behavioral patterns (such as purchase and
browsing history, likes, or reviews) and target them with personalized
product or content suggestions.
Recommender systems are a type of machine learning algorithm
designed to provide personalized suggestions by analyzing user data to
predict which items will be most relevant for each individual (snippet
2). They help narrow down options and improve the user experience by
tailoring recommendations based on preferences and behavior (snippet 3).
There are various models and approaches used in these systems, which can
be further explored in relevant courses or literature (snippet 1). It’s
always a good idea to verify important details from reliable
sources.
Collaborative Filtering
Collaborative filtering makes recommendations based on the
preferences and behaviors of similar users.
It analyzes patterns in user ratings, purchases, or interactions
to identify users with similar tastes.
The system then recommends items that similar users have liked or
interacted with.
Key advantages are that it can make serendipitous recommendations
and doesn’t require detailed item metadata.
Challenges include the cold-start problem (for new users/items)
and sparsity of user-item interaction data.
Content-Based Filtering
Content-based filtering makes recommendations based on the
attributes or features of the items themselves.
It analyzes the content, metadata, or descriptions of items a
user has liked in the past.
The system then recommends other items with similar content
characteristics.
Key advantages are that it can handle the cold-start problem and
doesn’t rely solely on user interactions.
Challenges include the need for rich item metadata and the
potential for overspecialization (recommending very similar
items).
Many modern recommender systems use a hybrid approach, combining
collaborative and content-based filtering to leverage the strengths of
each method.
- Collaborative Filtering Recommenders:
- Based on user-user or item-item similarities
- Make recommendations based on the preferences and behaviors of
similar users
- Examples: Amazon’s “Customers who bought this item also bought…”,
Netflix movie recommendations
- Content-Based Recommenders:
- Recommend items similar to the ones a user has liked in the
past
- Analyze the content, metadata, or descriptions of items
- Examples: Recommending books or articles based on the topics or
genres a user has previously engaged with
- Hybrid Recommenders:
- Combine collaborative and content-based approaches
- Can leverage the strengths of each to overcome individual
weaknesses
- Examples: Combining user preferences with item features to make
recommendations
- Knowledge-Based Recommenders:
- Make recommendations based on explicit knowledge about user
preferences and item features
- Use rule-based or case-based reasoning to match user needs with item
attributes
- Examples: Recommending products based on user-specified
requirements
- Demographic Recommenders:
- Make recommendations based on user demographic information
- Assume users with similar demographic profiles have similar
preferences
- Examples: Recommending products or content based on age, gender,
location, etc.
- Context-Aware Recommenders:
- Take into account the current context (time, location, device, etc.)
when making recommendations
- Adjust recommendations based on the user’s situation and
environment
- Examples: Suggesting nearby restaurants or events based on the
user’s current location
- E-commerce and Retail:
- Suggesting products or services based on a user’s browsing and
purchase history
- Personalizing the shopping experience and increasing sales
- Examples: Amazon’s “Customers who bought this also bought” and
Netflix’s movie recommendations
- Media and Entertainment:
- Suggesting movies, TV shows, music, books, or articles based on user
preferences
- Improving content discovery and engagement
- Examples: YouTube’s video recommendations and Spotify’s music
suggestions
- Social Media and Content Platforms:
- Recommending posts, accounts, or communities based on user interests
and social connections
- Increasing user engagement and time spent on the platform
- Examples: Facebook’s news feed recommendations and Twitter’s “Who to
Follow” suggestions
- Job and Talent Matching:
- Matching job seekers with relevant job postings based on their
skills and experience
- Helping employers find the best candidates for open positions
- Examples: LinkedIn’s job recommendations and recruitment platforms’
candidate matching
- Financial Services:
- Suggesting investment opportunities, financial products, or services
based on a user’s financial profile and goals
- Improving financial planning and decision-making
- Examples: Robo-advisors’ investment recommendations and banking
apps’ product suggestions
- Healthcare and Wellness:
- Recommending treatments, medications, or lifestyle changes based on
a patient’s medical history and symptoms
- Improving personalized healthcare and promoting healthy
behaviors
- Examples: Telemedicine platforms’ treatment recommendations and
fitness apps’ workout suggestions
- Education and Learning:
- Suggesting courses, learning materials, or educational resources
based on a student’s interests and performance
- Enhancing the learning experience and supporting personalized
education
- Examples: Online learning platforms’ course recommendations and
educational apps’ content suggestions
Recommender systems can be represented mathematically using the
following key components:
Users: Let U = {u1, u2, …, um} be the set of m
users.
Items: Let I = {i1, i2, …, in} be the set of n
items.
User-Item Interactions: Let R be the user-item
interaction matrix, where R[u, i] represents the rating, preference, or
interaction of user u with item i.
- R can be a sparse matrix, as users typically interact with only a
small subset of all available items.
- R can contain explicit ratings (e.g., 1-5 stars) or implicit
interactions (e.g., purchases, views, clicks).
Prediction Function: The goal of a recommender
system is to learn a prediction function f: U × I → R that estimates the
preference or rating of a user u for an item i.
- This function can be learned using various machine learning
techniques, such as matrix factorization, deep learning, or hybrid
approaches.
Recommendation Generation: Given a user u, the
recommender system generates a ranked list of items i ∈ I that the user
is most likely to interact with or prefer, based on the learned
prediction function f.
Evaluation Metrics: Recommender systems are
typically evaluated using metrics such as:
- Precision@k: The
fraction of the top-k recommended items that are relevant to the
user.
- Recall@k: The fraction
of relevant items that are included in the top-k recommendations.
- Normalized Discounted Cumulative Gain (NDCG): A measure of ranking
quality that considers the position of relevant items in the
recommendation list.
- Mean Squared Error (MSE) or Root Mean Squared Error (RMSE): Measures
the accuracy of rating predictions.
This mathematical representation provides a framework for
understanding the core components and objectives of recommender systems,
which can then be implemented using various algorithms and
techniques.
Recommender
Systems: Mathematical Representation
- Users and Items:
- Let U = {u1, u2, …, um} be the set of m users.
- Let I = {i1, i2, …, in} be the set of n items.
- User-Item Interactions:
- Let R be the user-item interaction matrix, where R[u, i] represents
the rating, preference, or interaction of user u with item i.
- R is typically a sparse matrix, as users interact with only a small
subset of all available items.
- R can contain explicit ratings (e.g., 1-5 stars) or implicit
interactions (e.g., purchases, views, clicks).
- Prediction Function:
- The goal is to learn a prediction function f: U × I → R that
estimates the preference or rating of a user u for an item
- This function can be learned using various machine learning
techniques, such as matrix factorization, deep learning, or hybrid
approaches.
- Recommendation Generation:
- Given a user u, the recommender system generates a ranked list of
items i ∈ I that the user is most likely to interact with or prefer,
based on the learned prediction function f.
- Evaluation Metrics:
- Precision@k, Recall@k, Normalized Discounted
Cumulative Gain (NDCG), Mean Squared Error (MSE), Root Mean Squared
Error (RMSE).
Singular
Value Decomposition (SVD) in Machine Learning
SVD is a powerful matrix factorization technique that can be used for
various machine learning tasks, including recommender systems.
Example: SVD for Collaborative Filtering in Recommender
Systems
- User-Item Interaction Matrix:
- Let R be the user-item interaction matrix, where R[u, i] represents
the rating or preference of user u for item i.
- SVD Decomposition:
- Decompose the user-item interaction matrix R into three matrices: U,
Σ, and V^T.
- R = UΣV^T, where:
- U is an m × m orthogonal matrix representing the left singular
vectors.
- Σ is an m × n diagonal matrix containing the singular values.
- V^T is an n × n orthogonal matrix representing the right singular
vectors.
- Recommendation Generation:
- To predict the rating or preference of a user u for an item i, use
the following formula:
- R[u, i] ≈ (U Σ V^T)[u, i]
- The top-k items with the highest predicted ratings can be
recommended to the user.
- Advantages of SVD:
- Handles the sparsity of the user-item interaction matrix.
- Captures the latent factors or hidden features that influence user
preferences.
- Provides a low-rank approximation of the original matrix, which can
improve computational efficiency.
- Can be combined with other techniques, such as regularization, to
improve the performance of the recommender system.
Example: SVD for Image Compression
- Image Representation:
- Let X be the m × n image matrix, where each element represents the
pixel intensity.
- SVD Decomposition:
- Decompose the image matrix X into three matrices: U, Σ, and
V^T.
- X = UΣV^T, where:
- U is an m × m orthogonal matrix representing the left singular
vectors.
- Σ is an m × n diagonal matrix containing the singular values.
- V^T is an n × n orthogonal matrix representing the right singular
vectors.
- Image Compression:
- Retain only the k largest singular values in Σ and the corresponding
columns in U and V^T.
- The compressed image can be reconstructed as X_compressed = U_k Σ_k
V_k^T, where the subscript k indicates the reduced-rank matrices.
- Advantages of SVD for Image Compression:
- Provides a low-rank approximation of the original image, reducing
the storage and transmission requirements.
- Preserves the most important features and structures of the image,
resulting in high-quality reconstructions.
- Can be used for various image processing tasks, such as denoising,
feature extraction, and dimensionality reduction.
SVD is a versatile technique that can be applied to a wide range of
machine learning problems, including recommender systems, image
processing, and data analysis. Understanding the mathematical
representation and examples of SVD is crucial for developing effective
and efficient machine learning solutions.
Study
Guide: Singular Value Decomposition (SVD) in Machine Learning
Introduction to SVD
Singular Value Decomposition (SVD) is a matrix factorization
technique used in various machine learning applications, including
dimensionality reduction, noise reduction, and collaborative filtering
in recommender systems.
Mathematical Representation
Given a matrix \(A\) of size \(m \times n\), SVD decomposes \(A\) into three matrices:
\[ A = U \Sigma V^T \]
- \(U\) is an \(m \times m\) orthogonal matrix. The columns
of \(U\) are the left singular vectors
of \(A\).
- \(\Sigma\) is an \(m \times n\) diagonal matrix with
non-negative real numbers on the diagonal. These numbers are the
singular values of \(A\).
- \(V^T\) is the transpose of an
\(n \times n\) orthogonal matrix. The
columns of \(V\) are the right singular
vectors of \(A\).
Properties
- The singular values in \(\Sigma\)
are sorted in descending order.
- The number of non-zero singular values is equal to the rank of the
matrix \(A\).
Applications of SVD in
Machine Learning
1. Dimensionality Reduction
- Principal Component Analysis (PCA): SVD is used to
compute the principal components of a dataset, which are the directions
of maximum variance. By projecting data onto the first few principal
components, we can reduce the dimensionality of the data while
preserving most of its variance.
2. Recommender Systems
- Collaborative Filtering: In recommender systems,
SVD is used to decompose the user-item interaction matrix into latent
factors. This helps in predicting missing entries (e.g., ratings) by
reconstructing the matrix using a reduced number of singular
values.
Example: Movie Recommendation
User-Item Matrix: Consider a matrix \(R\) where rows represent users and columns
represent movies. Each entry \(R[u,
i]\) is the rating given by user \(u\) to movie \(i\).
SVD Decomposition: Decompose \(R\) using SVD:
\[ R \approx U_k \Sigma_k V_k^T
\]
Here, \(U_k\), \(\Sigma_k\), and \(V_k^T\) are truncated matrices containing
only the top \(k\) singular values and
corresponding vectors.
Prediction: Predict the rating for a user-movie
pair by reconstructing the matrix:
\[ \hat{R} = U_k \Sigma_k V_k^T
\]
The predicted rating for user \(u\)
and movie \(i\) is \(\hat{R}[u, i]\).
3. Noise Reduction
- Image Compression: SVD can be used to compress
images by keeping only the largest singular values, which capture the
most significant features of the image, while discarding smaller
singular values that represent noise.
Practical Considerations
- Choosing \(k\):
The choice of \(k\) (number of singular
values to keep) is crucial. A smaller \(k\) reduces dimensionality but may lose
important information, while a larger \(k\) retains more information but may
include noise.
- Computational Complexity: SVD can be
computationally expensive for large matrices. Efficient algorithms and
approximations (e.g., truncated SVD) are often used in practice.
Conclusion
SVD is a powerful tool in machine learning for tasks involving matrix
factorization. Its ability to decompose matrices into meaningful
components makes it invaluable for applications like dimensionality
reduction, collaborative filtering, and noise reduction.
Machine
Learning Recommendation Systems: Study Guide
I. Core Recommendation
System Types
A. Collaborative Filtering
- User-Based (UBCF)
Mathematical representation:
pred(u,i) = mean(ratings_u) + Σ(sim(u,v) × (rating_v,i - mean(ratings_v)))
where:
- pred(u,i) is the prediction for user u on item i
- sim(u,v) is the similarity between users u and v
- Item-Based (IBCF)
Mathematical representation:
pred(u,i) = Σ(sim(i,j) × rating_u,j) / Σ|sim(i,j)|
where:
- sim(i,j) is the similarity between items i and j
- Model-Based
Uses machine learning models to predict ratings
Common approach: Matrix Factorization
R ≈ P × Q^T
where:
- R is the user-item rating matrix
- P is the user latent factor matrix
- Q is the item latent factor matrix
B. Content-Based Filtering
- Feature Extraction
Text: TF-IDF representation
TF-IDF(t,d) = TF(t,d) × IDF(t)
where:
- TF(t,d) is term frequency
- IDF(t) is inverse document frequency
Images: CNN features
- Profile Learning
- Methods:
- Decision Trees
- Naive Bayes
- Neural Networks
- SVM
- Regression Models
II. Advanced Techniques
A. Matrix Factorization
SVD (Singular Value Decomposition)
A = U Σ V^T
where:
- A is the original matrix
- U contains left singular vectors
- Σ contains singular values
- V^T contains right singular vectors
ALS (Alternating Least Squares)
Minimize: Σ(r_ui - p_u^T q_i)^2 + λ(||p_u||^2 + ||q_i||^2)
where:
- r_ui is the rating of user u for item i
- p_u is the user latent factor
- q_i is the item latent factor
- λ is the regularization parameter
B. Deep Learning Approaches
- Neural Collaborative Filtering
- Autoencoders
- RNNs for Sequential Recommendations
- CNNs for Feature Learning
III. Evaluation Metrics
Accuracy Metrics
RMSE = √(Σ(y_true - y_pred)^2 / n)
MAE = Σ|y_true - y_pred| / n
Ranking Metrics
Precision@k = relevant_items@k / k
Recall@k = relevant_items@k / total_relevant_items
NDCG@k = DCG@k / IDCG@k
IV. Implementation
Considerations
- Cold Start Problem
- Solutions:
- Hybrid approaches
- Content-based initialization
- Default recommendations
- Scalability
- Techniques:
- Dimensionality reduction
- Sampling
- Distributed computing
- Real-time Updates
- Online learning
- Incremental updates
- Stream processing
V. Future Trends
- Deep Learning Integration
- Reinforcement Learning
- Graph Neural Networks
- Natural Language Processing
- Federated Learning
- Explainable AI
VI. Benefits and
Applications
- Personalized Content Delivery
- Increased User Engagement
- Revenue Growth
- Improved User Experience
- Automated Decision Making
- Scalable Solutions
svd
study
another
good link
LS0tDQp0aXRsZTogIlNWRCBsZWN0dXJlOiBNTDczMzE6IDIwX25vdl8yMDI0Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCjwhLS0gR29vZ2xlIHRhZyAoZ3RhZy5qcykgLS0+DQoNCmBgYHs9aHRtbH0NCjxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3d3dy5nb29nbGV0YWdtYW5hZ2VyLmNvbS9ndGFnL2pzP2lkPUctQ1YyNjQ4R1FNSyI+PC9zY3JpcHQ+DQpgYGANCg0KYGBgez1odG1sfQ0KPHNjcmlwdD4NCiAgd2luZG93LmRhdGFMYXllciA9IHdpbmRvdy5kYXRhTGF5ZXIgfHwgW107DQogIGZ1bmN0aW9uIGd0YWcoKXtkYXRhTGF5ZXIucHVzaChhcmd1bWVudHMpO30NCiAgZ3RhZygnanMnLCBuZXcgRGF0ZSgpKTsNCg0KICBndGFnKCdjb25maWcnLCAnRy1DVjI2NDhHUU1LJyk7DQo8L3NjcmlwdD4NCmBgYA0KDQpTdHJlYW0gTmFtZSBUZXhhc2NoaWtraXRhIFN0cmVhbSBVUkwgPGh0dHBzOi8vcnB1YnMuY29tL1RleGFzY2hpa2tpdGE+DQpTdHJlYW0gSUQgOTk2MjMyNDE3OSBNZWFzdXJlbWVudCBJZCBHLUNWMjY0OEdRTUsNCg0KIyMjICoqU3R1ZHkgR3VpZGU6IFJlY29tbWVuZGVyIFN5c3RlbXMgVXNpbmcgU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSoqDQoNCiMjIyMgKipLZXkgQ29uY2VwdHMqKg0KDQoxLiAgKipSZWNvbW1lbmRlciBTeXN0ZW1zKio6DQogICAgLSAgIFN5c3RlbXMgZGVzaWduZWQgdG8gc3VnZ2VzdCBpdGVtcyAoZS5nLiwgbW92aWVzKSBiYXNlZCBvbiB1c2VyDQogICAgICAgIHByZWZlcmVuY2VzIG9yIGhpc3RvcnkuDQogICAgLSAgIEV4YW1wbGU6IE5ldGZsaXggc3VnZ2VzdGluZyBtb3ZpZXMgYmFzZWQgb24gdXNlciByYXRpbmdzIGFuZA0KICAgICAgICB3YXRjaCBoaXN0b3J5Lg0KMi4gICoqTWF0cml4IFJlcHJlc2VudGF0aW9uKio6DQogICAgLSAgIERhdGEgaXMgcmVwcmVzZW50ZWQgYXMgYSBtYXRyaXg6DQogICAgICAgIC0gICBSb3dzOiBVc2Vycy4NCiAgICAgICAgLSAgIENvbHVtbnM6IE1vdmllcy4NCiAgICAgICAgLSAgIEVudHJpZXM6IFJhdGluZ3MgKG9yIDAgaWYgbm8gcmF0aW5nKS4NCjMuICAqKk1hdHJpeCBGYWN0b3JpemF0aW9uKio6DQogICAgLSAgIERlY29tcG9zaW5nIHRoZSBtYXRyaXggaW50byB0aHJlZSBzbWFsbGVyIG1hdHJpY2VzOg0KICAgICAgICAtICAgJEEgPSBVIFxTaWdtYSBWXlQkDQogICAgICAgIC0gICAkVSQ6IFVzZXIgbGF0ZW50IGZhY3RvcnMgKHVzZXIgZW1iZWRkaW5nKS4NCiAgICAgICAgLSAgICRcU2lnbWEkOiBEaWFnb25hbCBtYXRyaXggd2l0aCBzaW5ndWxhciB2YWx1ZXMuDQogICAgICAgIC0gICAkViQ6IE1vdmllIGxhdGVudCBmYWN0b3JzIChtb3ZpZSBlbWJlZGRpbmcpLg0KNC4gICoqU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSoqOg0KICAgIC0gICBBIG1hdGhlbWF0aWNhbCBtZXRob2QgdG8gZGVjb21wb3NlIGEgbWF0cml4Lg0KICAgIC0gICBDYXB0dXJlcyBsYXRlbnQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHVzZXJzIGFuZCBtb3ZpZXMuDQogICAgLSAgIFJlbW92ZXMgbm9pc2UgYnkgY29uc2lkZXJpbmcgdG9wIHNpbmd1bGFyIHZhbHVlcyAoZGltZW5zaW9uYWxpdHkNCiAgICAgICAgcmVkdWN0aW9uKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipNYXRoZW1hdGljYWwgUmVwcmVzZW50YXRpb24qKg0KDQpHaXZlbiBhIG1hdHJpeCAkQSQ6IDEuICoqRGVjb21wb3NpdGlvbioqOiAkQSA9IFUgXFNpZ21hIFZeVCQsIHdoZXJlOiAtDQokVSQgYW5kICRWJCBhcmUgb3J0aG9nb25hbCBtYXRyaWNlcy4gLSAkXFNpZ21hJCBpcyBhIGRpYWdvbmFsIG1hdHJpeA0Kd2l0aCBzaW5ndWxhciB2YWx1ZXMgaW4gZGVzY2VuZGluZyBvcmRlci4NCg0KMi4gICoqSW50ZXJwcmV0YXRpb24qKjoNCiAgICAtICAgJFUkOiBVc2VyIGxhdGVudCBmYWN0b3JzLg0KICAgIC0gICAkViQ6IE1vdmllIGxhdGVudCBmYWN0b3JzLg0KICAgIC0gICAkXFNpZ21hJDogU3RyZW5ndGggb2YgbGF0ZW50IGZhY3RvcnMuDQozLiAgKipDb3NpbmUgU2ltaWxhcml0eSoqOiBVc2VkIHRvIG1lYXN1cmUgc2ltaWxhcml0eSBiZXR3ZWVuIGl0ZW1zOiAkJA0KICAgIFx0ZXh0e0Nvc2luZSBTaW1pbGFyaXR5fSA9IFxmcmFje1xtYXRoYmZ7YX0gXGNkb3QgXG1hdGhiZntifX17XHxcbWF0aGJme2F9XHwgXHxcbWF0aGJme2J9XHx9DQogICAgJCQNCiAgICAtICAgQXBwbGllZCB0byBjb2x1bW5zIG9mICRWJCB0byBmaW5kIHNpbWlsYXIgbW92aWVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkltcGxlbWVudGF0aW9uIFN0ZXBzKioNCg0KMS4gICoqTG9hZCBEYXRhKio6DQogICAgLSAgIEV4dHJhY3QgdXNlci1tb3ZpZSByYXRpbmdzLCBtb3ZpZSBkZXRhaWxzLg0KMi4gICoqQ3JlYXRlIFJhdGluZyBNYXRyaXgqKjoNCiAgICAtICAgQ29udmVydCB1c2VyLW1vdmllIGludGVyYWN0aW9ucyBpbnRvIGEgbWF0cml4IHdpdGggcm93cyBhcyB1c2Vycw0KICAgICAgICBhbmQgY29sdW1ucyBhcyBtb3ZpZXMuDQozLiAgKipOb3JtYWxpemUgRGF0YSoqOg0KICAgIC0gICBOb3JtYWxpemUgcmF0aW5ncyBmb3IgZWFjaCBtb3ZpZSB0byBoYW5kbGUgdmFyaWF0aW9ucy4NCjQuICAqKkNvbXB1dGUgU1ZEKio6DQogICAgLSAgIFVzZSBsaWJyYXJpZXMgKGUuZy4sIE51bVB5IG9yIFNjaVB5KSB0byBkZWNvbXBvc2UgdGhlIG1hdHJpeA0KICAgICAgICBpbnRvICRVLCBcU2lnbWEsJCBhbmQgJFZeVCQuDQo1LiAgKipGaW5kIFJlY29tbWVuZGF0aW9ucyoqOg0KICAgIC0gICBVc2UgdG9wIHNpbmd1bGFyIHZhbHVlcyBhbmQgY29ycmVzcG9uZGluZyB2ZWN0b3JzIHRvOg0KICAgICAgICAtICAgUmVjb21tZW5kIG1vdmllcyBmb3IgYSB1c2VyLg0KICAgICAgICAtICAgRmluZCBzaW1pbGFyIG1vdmllcy4NCjYuICAqKk9wdGltaXplIHdpdGggUmVkdWNlZCBTVkQqKjoNCiAgICAtICAgVXNlIG9ubHkgdG9wICRrJCBzaW5ndWxhciB2YWx1ZXMgKGUuZy4sIGJhc2VkIG9uIGVsYm93IHBvaW50IGluDQogICAgICAgIHNpbmd1bGFyIHZhbHVlIHBsb3QpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKlZpc3VhbGl6YXRpb24qKg0KDQoxLiAgKipNYXRyaXggRGVjb21wb3NpdGlvbioqOg0KICAgIC0gICBPcmlnaW5hbCBtYXRyaXggJEEkIOKGkiAkVSBcU2lnbWEgVl5UJC4NCiAgICAtICAgJFxTaWdtYSQ6IFNpbmd1bGFyIHZhbHVlcyBwbG90dGVkIHRvIGlkZW50aWZ5IHRoZSBlbGJvdyBwb2ludA0KICAgICAgICAob3B0aW1hbCAkayQpLg0KMi4gICoqUmVjb21tZW5kYXRpb24gRXhhbXBsZSoqOg0KICAgIC0gICBWaXN1YWxpemUgdG9wIHJlY29tbWVuZGF0aW9ucyBmb3IgYSBtb3ZpZSBvciB1c2VyLg0KDQojIyMjICoqQ29kZSBFeGFtcGxlKioNCg0KYGBgIHB5dGhvbg0KaW1wb3J0IG51bXB5IGFzIG5wDQpmcm9tIG51bXB5LmxpbmFsZyBpbXBvcnQgc3ZkDQpmcm9tIHNrbGVhcm4ubWV0cmljcy5wYWlyd2lzZSBpbXBvcnQgY29zaW5lX3NpbWlsYXJpdHkNCg0KIyBTdGVwIDE6IENvbnN0cnVjdCByYXRpbmcgbWF0cml4DQpyYXRpbmdzX21hdHJpeCA9IG5wLnplcm9zKChudW1fdXNlcnMsIG51bV9tb3ZpZXMpKSAgIyBFeGFtcGxlOiBGaWxsIHdpdGggYWN0dWFsIHJhdGluZ3MgZGF0YQ0KDQojIFN0ZXAgMjogTm9ybWFsaXplIHJhdGluZ3MNCm1lYW5fcmF0aW5ncyA9IG5wLm1lYW4ocmF0aW5nc19tYXRyaXgsIGF4aXM9MCkNCm5vcm1hbGl6ZWRfbWF0cml4ID0gcmF0aW5nc19tYXRyaXggLSBtZWFuX3JhdGluZ3MNCg0KIyBTdGVwIDM6IENvbXB1dGUgU1ZEDQpVLCBTLCBWVCA9IHN2ZChub3JtYWxpemVkX21hdHJpeCwgZnVsbF9tYXRyaWNlcz1GYWxzZSkNClNfZGlhZyA9IG5wLmRpYWcoUykNCg0KIyBTdGVwIDQ6IENob29zZSB0b3AgayBjb21wb25lbnRzIChyZWR1Y2Ugbm9pc2UpDQprID0gMzAgICMgT3B0aW1hbCBudW1iZXIgb2Ygc2luZ3VsYXIgdmFsdWVzDQpVX2sgPSBVWzosIDprXQ0KU19rID0gU19kaWFnWzprLCA6a10NClZUX2sgPSBWVFs6aywgOl0NCg0KIyBTdGVwIDU6IFJlY29tbWVuZGF0aW9ucw0KZGVmIHJlY29tbWVuZF9tb3ZpZXMobW92aWVfaWQsIHRvcF9uPTEwKToNCiAgICBtb3ZpZV92ZWN0b3IgPSBWVF9rWzosIG1vdmllX2lkXQ0KICAgIHNpbWlsYXJpdGllcyA9IGNvc2luZV9zaW1pbGFyaXR5KG1vdmllX3ZlY3Rvci5yZXNoYXBlKDEsIC0xKSwgVlRfay5UKQ0KICAgIHRvcF9pbmRpY2VzID0gbnAuYXJnc29ydChzaW1pbGFyaXRpZXNbMF0pWy10b3BfbjpdDQogICAgcmV0dXJuIHRvcF9pbmRpY2VzWzo6LTFdDQoNCiMgRXhhbXBsZTogUmVjb21tZW5kIG1vdmllcyBzaW1pbGFyIHRvIFRveSBTdG9yeSAobW92aWVfaWQ9MSkNCnNpbWlsYXJfbW92aWVzID0gcmVjb21tZW5kX21vdmllcyhtb3ZpZV9pZD0xKQ0KcHJpbnQoIlJlY29tbWVuZGVkIE1vdmllczoiLCBzaW1pbGFyX21vdmllcykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkFwcGxpY2F0aW9ucyoqDQoNCjEuICAqKk1vdmllIFJlY29tbWVuZGF0aW9ucyoqOg0KICAgIC0gICBTdWdnZXN0IG1vdmllcyBmb3IgdXNlcnMgKE5ldGZsaXgtc3R5bGUpLg0KICAgIC0gICBGaW5kIHNpbWlsYXIgbW92aWVzIChjb250ZW50LWJhc2VkKS4NCjIuICAqKkFkdmVydGlzaW5nKio6DQogICAgLSAgIFJlY29tbWVuZCBhZHMgdG8gdXNlcnMgd2l0aCBzaW1pbGFyIGludGVyZXN0cy4NCjMuICAqKk5vaXNlIFJlZHVjdGlvbioqOg0KICAgIC0gICBIYW5kbGUgbm9pc3kgZGF0YSBieSBmb2N1c2luZyBvbiB0b3Agc2luZ3VsYXIgdmFsdWVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkluc2lnaHRzKioNCg0KLSAgICoqQWR2YW50YWdlcyoqOg0KICAgIC0gICBTaW1wbGUsIGludGVycHJldGFibGUsIGFuZCBlZmZlY3RpdmUgZm9yIGNvbGxhYm9yYXRpdmUNCiAgICAgICAgZmlsdGVyaW5nLg0KICAgIC0gICBDYW4gaGFuZGxlIHNwYXJzZSBkYXRhLg0KLSAgICoqTGltaXRhdGlvbnMqKjoNCiAgICAtICAgQ29sZCBzdGFydCBwcm9ibGVtOiBDYW5ub3QgaGFuZGxlIG5ldyB1c2Vycy9tb3ZpZXMgd2l0aG91dCBwcmlvcg0KICAgICAgICBkYXRhLg0KICAgIC0gICBObyBnZW5yZSBvciBtZXRhZGF0YSB1c2FnZSB1bmxlc3MgZXh0ZW5kZWQuDQoNCkJ5IHVuZGVyc3RhbmRpbmcgU1ZELCB5b3UgY2FuIGltcGxlbWVudCBlZmZpY2llbnQgcmVjb21tZW5kZXIgc3lzdGVtcw0KYW5kIGV4cGxvcmUgYWR2YW5jZWQgdGVjaG5pcXVlcyBsaWtlIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgb3INCm5ldXJhbCBuZXR3b3JrLWJhc2VkIG1vZGVscyBmb3IgaW1wcm92ZWQgcmVzdWx0cy4NCg0KIyMjIFN1bW1hcnkgb2YgTGVjdHVyZSBvbiBTVkQgZm9yIFJlY29tbWVuZGVyIFN5c3RlbXMNCg0KIyMjIyAqKktleSBQb2ludHMqKg0KDQoxLiAgKipJbnRyb2R1Y3Rpb24gdG8gU1ZEKio6DQogICAgLSAgIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaXMgdXNlZCBpbiByZWNvbW1lbmRhdGlvbg0KICAgICAgICBzeXN0ZW1zIHRvIGRlY29tcG9zZSB1c2VyLWl0ZW0gbWF0cmljZXMgaW50byB0aHJlZSBtYXRyaWNlcyAkVSQsDQogICAgICAgICRcU2lnbWEkLCBhbmQgJFZeVCQuDQogICAgLSAgIFRoaXMgcHJvY2VzcyBwcm92aWRlcyB2ZWN0b3IgcmVwcmVzZW50YXRpb25zIChlbWJlZGRpbmdzKSBvZg0KICAgICAgICB1c2VycyBhbmQgaXRlbXMgdGhhdCBjYXB0dXJlIGxhdGVudCByZWxhdGlvbnNoaXBzLg0KMi4gICoqRGF0YXNldCBEZXNjcmlwdGlvbioqOg0KICAgIC0gICBJbnB1dDogVXNlciByYXRpbmdzIG9mIG1vdmllcy4NCiAgICAtICAgU3RydWN0dXJlOg0KICAgICAgICAtICAgVXNlcnMgKHJvd3MpIMOXIE1vdmllcyAoY29sdW1ucykgbWF0cml4IHdoZXJlIGVudHJpZXMNCiAgICAgICAgICAgIHJlcHJlc2VudCByYXRpbmdzLg0KICAgICAgICAtICAgUmF0aW5ncyBhcmUgbm9ybWFsaXplZCBmb3IgY29uc2lzdGVuY3kuDQozLiAgKipNYXRyaXggRmFjdG9yaXphdGlvbioqOg0KICAgIC0gICBNYXRyaXggJEEkICh1c2VyLW1vdmllIHJhdGluZ3MpIGlzIGZhY3Rvcml6ZWQgYXM6ICQkDQogICAgICAgIEEgPSBVIFxTaWdtYSBWXlQNCiAgICAgICAgJCQNCiAgICAgICAgLSAgICRVJDogRW1iZWRkaW5nIGZvciB1c2Vycy4NCiAgICAgICAgLSAgICRWJDogRW1iZWRkaW5nIGZvciBtb3ZpZXMuDQogICAgICAgIC0gICAkXFNpZ21hJDogRGlhZ29uYWwgbWF0cml4IGNvbnRhaW5pbmcgc2luZ3VsYXIgdmFsdWVzIHRoYXQNCiAgICAgICAgICAgIHJlcHJlc2VudCB0aGUgc3RyZW5ndGggb2YgY29ycmVzcG9uZGluZyBsYXRlbnQgZmVhdHVyZXMuDQo0LiAgKipEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24qKjoNCiAgICAtICAgTm9pc2UgcmVkdWN0aW9uIGlzIGFjaGlldmVkIGJ5IHNlbGVjdGluZyB0b3AgJGskIHNpbmd1bGFyIHZhbHVlcw0KICAgICAgICAoJFxTaWdtYV9rJCkgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgdmVjdG9ycy4NCiAgICAtICAgVGhpcyBhcHByb3hpbWF0ZXMgJEEkIGFzOiAkJA0KICAgICAgICBBX2sgXGFwcHJveCBVX2sgXFNpZ21hX2sgVl9rXlQNCiAgICAgICAgJCQNCjUuICAqKkFwcGxpY2F0aW9ucyoqOg0KICAgIC0gICAqKlJlY29tbWVuZGF0aW9uKio6IEZpbmQgbW92aWVzIHNpbWlsYXIgdG8gYSBnaXZlbiBtb3ZpZS4NCiAgICAtICAgKipEZW5vaXNpbmcqKjogUmVkdWNlIHRoZSBlZmZlY3Qgb2Ygbm9pc3ksIGxlc3MgcmVsZXZhbnQNCiAgICAgICAgZmVhdHVyZXMuDQogICAgLSAgICoqQ29sZCBTdGFydCBQcm9ibGVtKio6IEFkZHJlc3MgbmV3IHVzZXIvbW92aWUgc2NlbmFyaW9zIHdpdGgNCiAgICAgICAgZGVmYXVsdCByZWNvbW1lbmRhdGlvbnMuDQo2LiAgKipJbXBsZW1lbnRhdGlvbioqOg0KICAgIC0gICBVc2UgUHl0aG9uJ3MgYG51bXB5LmxpbmFsZy5zdmRgIGZvciBTVkQgY29tcHV0YXRpb24uDQogICAgLSAgIFNlbGVjdCB0aGUgb3B0aW1hbCAkayQgYnkgZXhhbWluaW5nIHRoZSBzaW5ndWxhciB2YWx1ZXMnDQogICAgICAgIGRyb3Atb2ZmIChlbGJvdyBtZXRob2QpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFN0dWR5IEd1aWRlDQoNCiMjIyMgKipNYXRoZW1hdGljYWwgQmFja2dyb3VuZCoqDQoNCjEuICAqKlNWRCBSZXByZXNlbnRhdGlvbioqOg0KICAgIC0gICBEZWNvbXBvc2UgYSBtYXRyaXggJEEkOiAkJA0KICAgICAgICBBID0gVSBcU2lnbWEgVl5UDQogICAgICAgICQkDQogICAgLSAgIFByb3BlcnRpZXM6DQogICAgICAgIC0gICAkVSQgYW5kICRWXlQkIGFyZSBvcnRob2dvbmFsIG1hdHJpY2VzLg0KICAgICAgICAtICAgJFxTaWdtYSQgY29udGFpbnMgc2luZ3VsYXIgdmFsdWVzIHNvcnRlZCBpbiBkZXNjZW5kaW5nDQogICAgICAgICAgICBvcmRlci4NCjIuICAqKkludGVycHJldGF0aW9uKio6DQogICAgLSAgICRVJDogUmVwcmVzZW50cyB1c2VyIGVtYmVkZGluZ3MuDQogICAgLSAgICRWXlQkOiBSZXByZXNlbnRzIGl0ZW0gZW1iZWRkaW5ncy4NCiAgICAtICAgJFxTaWdtYSQ6IEltcG9ydGFuY2Ugb2YgZmVhdHVyZXMgKGhpZ2hlciB2YWx1ZXMgPSBtb3JlDQogICAgICAgIGltcG9ydGFuY2UpLg0KMy4gICoqRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uKio6DQogICAgLSAgIFNlbGVjdCB0b3AgJGskIHNpbmd1bGFyIHZhbHVlcy4NCiAgICAtICAgUmVkdWNlZCBtYXRyaXg6ICQkDQogICAgICAgIEFfayA9IFVfayBcU2lnbWFfayBWX2teVA0KICAgICAgICAkJA0KDQojIyMjICoqSW1wbGVtZW50YXRpb24gU3RlcHMqKg0KDQoxLiAgKipEYXRhIFByZXBhcmF0aW9uKio6DQoNCiAgICAtICAgQ29uc3RydWN0IGEgdXNlci1tb3ZpZSBtYXRyaXggd2l0aCByYXRpbmdzLg0KICAgIC0gICBOb3JtYWxpemUgdGhlIHJhdGluZ3MuDQoNCjIuICAqKkNvbXB1dGUgU1ZEKio6DQoNCiAgICBgYGAgcHl0aG9uDQogICAgaW1wb3J0IG51bXB5IGFzIG5wDQoNCiAgICAjIENvbXB1dGUgU1ZEDQogICAgVSwgUywgVnQgPSBucC5saW5hbGcuc3ZkKEEsIGZ1bGxfbWF0cmljZXM9RmFsc2UpDQoNCiAgICAjIFNlbGVjdCB0b3AgayBjb21wb25lbnRzDQogICAgayA9IDMwDQogICAgVV9rID0gVVs6LCA6a10NCiAgICBTX2sgPSBucC5kaWFnKFNbOmtdKQ0KICAgIFZ0X2sgPSBWdFs6aywgOl0NCiAgICBgYGANCg0KMy4gICoqQ29zaW5lIFNpbWlsYXJpdHkqKjoNCg0KICAgIC0gICBGaW5kIHNpbWlsYXIgbW92aWVzIHVzaW5nIGNvc2luZSBzaW1pbGFyaXR5IGJldHdlZW4gbW92aWUNCiAgICAgICAgdmVjdG9ycyBpbiAkVl9rJC4NCg0KICAgIGBgYCBweXRob24NCiAgICBmcm9tIHNrbGVhcm4ubWV0cmljcy5wYWlyd2lzZSBpbXBvcnQgY29zaW5lX3NpbWlsYXJpdHkNCg0KICAgICMgQ29tcHV0ZSBjb3NpbmUgc2ltaWxhcml0eQ0KICAgIHNpbWlsYXJpdGllcyA9IGNvc2luZV9zaW1pbGFyaXR5KFZ0X2suVCkNCg0KICAgICMgUmVjb21tZW5kIHRvcCBuIG1vdmllcw0KICAgIG1vdmllX2lkID0gMCAgIyBNb3ZpZSBvZiBpbnRlcmVzdA0KICAgIHRvcF9uID0gMTANCiAgICBzaW1pbGFyX21vdmllcyA9IG5wLmFyZ3NvcnQoc2ltaWxhcml0aWVzW21vdmllX2lkXSlbLXRvcF9uOl0NCiAgICBgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyBWaXN1YWxpemF0aW9uDQoNCiMjIyMgKipTaW5ndWxhciBWYWx1ZXMgKFNjcmVlIFBsb3QpKioNCg0KLSAgIFBsb3QgdGhlIHNpbmd1bGFyIHZhbHVlcyB0byBpZGVudGlmeSB0aGUgZWxib3cgcG9pbnQgd2hlcmUgbW9zdA0KICAgIGluZm9ybWF0aW9uIGlzIGNhcHR1cmVkOg0KDQogICAgYGBgIHB5dGhvbg0KICAgIGltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KICAgIHBsdC5wbG90KFMpDQogICAgcGx0LnhsYWJlbCgnQ29tcG9uZW50IEluZGV4JykNCiAgICBwbHQueWxhYmVsKCdTaW5ndWxhciBWYWx1ZScpDQogICAgcGx0LnRpdGxlKCdTY3JlZSBQbG90JykNCiAgICBwbHQuc2hvdygpDQogICAgYGBgDQoNCiMjIyMgKipSZWNvbW1lbmRhdGlvbiBFeGFtcGxlKioNCg0KLSAgIEhlYXRtYXAgb2YgdXNlci1pdGVtIGludGVyYWN0aW9uIGJlZm9yZSBhbmQgYWZ0ZXIgZGltZW5zaW9uYWxpdHkNCiAgICByZWR1Y3Rpb246DQoNCiAgICBgYGAgcHl0aG9uDQogICAgaW1wb3J0IHNlYWJvcm4gYXMgc25zDQoNCiAgICBzbnMuaGVhdG1hcChBLCBjbWFwPSdjb29sd2FybScsIGNiYXI9VHJ1ZSkNCiAgICBzbnMuaGVhdG1hcChVX2sgQCBTX2sgQCBWdF9rLCBjbWFwPSdjb29sd2FybScsIGNiYXI9VHJ1ZSkNCiAgICBgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyBBZGRpdGlvbmFsIFRpcHMNCg0KMS4gICoqT3B0aW1hbCoqICRrJDoNCiAgICAtICAgVXNlIHRoZSAiZWxib3cgbWV0aG9kIiBvciBjcm9zcy12YWxpZGF0aW9uIG9uIGEgc3Vic2V0IG9mIGRhdGEuDQoyLiAgKipDb2xkIFN0YXJ0IEhhbmRsaW5nKio6DQogICAgLSAgIEluaXRpYWxpemUgbmV3IHVzZXJzL21vdmllcyB3aXRoIGF2ZXJhZ2VzIG9yIG1vc3QgcG9wdWxhciBpdGVtcy4NCjMuICAqKkNvbXBhcmlzb24gdG8gUENBKio6DQogICAgLSAgIEJvdGggUENBIGFuZCBTVkQgcmVkdWNlIGRpbWVuc2lvbnMgYnkgY2FwdHVyaW5nIG1heGltdW0NCiAgICAgICAgdmFyaWFuY2U7IFNWRCBpcyBtb3JlIGdlbmVyYWwgYW5kIGRpcmVjdGx5IGFwcGxpY2FibGUgdG8NCiAgICAgICAgdXNlci1pdGVtIG1hdHJpY2VzLg0KNC4gICoqRXh0ZW5zaW9ucyoqOg0KICAgIC0gICBJbmNvcnBvcmF0ZSBnZW5yZXMgb3IgdXNlciBkZW1vZ3JhcGhpY3MgZm9yIGh5YnJpZA0KICAgICAgICByZWNvbW1lbmRhdGlvbiBzeXN0ZW1zLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFNhbXBsZSBDb2RlIChQeXRob24pDQoNCmBgYCBweXRob24NCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBza2xlYXJuLm1ldHJpY3MucGFpcndpc2UgaW1wb3J0IGNvc2luZV9zaW1pbGFyaXR5DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgU2FtcGxlIHVzZXItaXRlbSBtYXRyaXgNCkEgPSBucC5hcnJheShbWzUsIDQsIDAsIDFdLA0KICAgICAgICAgICAgICBbNCwgMCwgMCwgMV0sDQogICAgICAgICAgICAgIFsxLCAxLCAwLCA1XSwNCiAgICAgICAgICAgICAgWzEsIDAsIDQsIDRdLA0KICAgICAgICAgICAgICBbMCwgMSwgNSwgNF1dKQ0KDQojIFBlcmZvcm0gU1ZEDQpVLCBTLCBWdCA9IG5wLmxpbmFsZy5zdmQoQSwgZnVsbF9tYXRyaWNlcz1GYWxzZSkNCg0KIyBTZWxlY3QgdG9wLWsgY29tcG9uZW50cw0KayA9IDINClVfayA9IFVbOiwgOmtdDQpTX2sgPSBucC5kaWFnKFNbOmtdKQ0KVnRfayA9IFZ0WzprLCA6XQ0KDQojIFJlY29uc3RydWN0IHJlZHVjZWQgbWF0cml4DQpBX2sgPSBVX2sgQCBTX2sgQCBWdF9rDQoNCiMgVmlzdWFsaXplIG9yaWdpbmFsIGFuZCByZWR1Y2VkIG1hdHJpY2VzDQpwbHQuc3VicGxvdCgxLCAyLCAxKQ0KcGx0LnRpdGxlKCJPcmlnaW5hbCBNYXRyaXgiKQ0Kc25zLmhlYXRtYXAoQSwgYW5ub3Q9VHJ1ZSwgY21hcD0nY29vbHdhcm0nKQ0KDQpwbHQuc3VicGxvdCgxLCAyLCAyKQ0KcGx0LnRpdGxlKCJSZWR1Y2VkIE1hdHJpeCIpDQpzbnMuaGVhdG1hcChBX2ssIGFubm90PVRydWUsIGNtYXA9J2Nvb2x3YXJtJykNCnBsdC5zaG93KCkNCmBgYA0KDQpUaGlzIGNvZGUgaW1wbGVtZW50cyB0aGUgY29uY2VwdHMgZGlzY3Vzc2VkIGFuZCBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YNCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvbiByZWNvbW1lbmRhdGlvbnMuDQoNCi0gICBNYWNoaW5lIGxlYXJuaW5nLWJhc2VkIHJlY29tbWVuZGF0aW9uIHN5c3RlbXMgYXJlIHBvd2VyZnVsIGVuZ2luZXMNCiAgICB1c2luZyBtYWNoaW5lIGxlYXJuaW5nIChNTCkgYWxnb3JpdGhtcyB0byBzZWdtZW50IGN1c3RvbWVycyBiYXNlZCBvbg0KICAgIHVzZXIgZGF0YSBhbmQgYmVoYXZpb3JhbCBwYXR0ZXJucyAoc3VjaCBhcyBwdXJjaGFzZSBhbmQgYnJvd3NpbmcNCiAgICBoaXN0b3J5LCBsaWtlcywgb3IgcmV2aWV3cykgYW5kIHRhcmdldCB0aGVtIHdpdGggcGVyc29uYWxpemVkDQogICAgcHJvZHVjdCBvciBjb250ZW50IHN1Z2dlc3Rpb25zLg0KDQotICAgUmVjb21tZW5kZXIgc3lzdGVtcyBhcmUgYSB0eXBlIG9mIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtDQogICAgZGVzaWduZWQgdG8gcHJvdmlkZSBwZXJzb25hbGl6ZWQgc3VnZ2VzdGlvbnMgYnkgYW5hbHl6aW5nIHVzZXIgZGF0YQ0KICAgIHRvIHByZWRpY3Qgd2hpY2ggaXRlbXMgd2lsbCBiZSBtb3N0IHJlbGV2YW50IGZvciBlYWNoIGluZGl2aWR1YWwNCiAgICAoc25pcHBldCAyKS4gVGhleSBoZWxwIG5hcnJvdyBkb3duIG9wdGlvbnMgYW5kIGltcHJvdmUgdGhlIHVzZXINCiAgICBleHBlcmllbmNlIGJ5IHRhaWxvcmluZyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gcHJlZmVyZW5jZXMgYW5kDQogICAgYmVoYXZpb3IgKHNuaXBwZXQgMykuIFRoZXJlIGFyZSB2YXJpb3VzIG1vZGVscyBhbmQgYXBwcm9hY2hlcyB1c2VkDQogICAgaW4gdGhlc2Ugc3lzdGVtcywgd2hpY2ggY2FuIGJlIGZ1cnRoZXIgZXhwbG9yZWQgaW4gcmVsZXZhbnQgY291cnNlcw0KICAgIG9yIGxpdGVyYXR1cmUgKHNuaXBwZXQgMSkuIEl0J3MgYWx3YXlzIGEgZ29vZCBpZGVhIHRvIHZlcmlmeQ0KICAgIGltcG9ydGFudCBkZXRhaWxzIGZyb20gcmVsaWFibGUgc291cmNlcy4NCg0KLSAgICoqQ29sbGFib3JhdGl2ZSBGaWx0ZXJpbmcqKg0KDQogICAgLSAgIENvbGxhYm9yYXRpdmUgZmlsdGVyaW5nIG1ha2VzIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB0aGUNCiAgICAgICAgcHJlZmVyZW5jZXMgYW5kIGJlaGF2aW9ycyBvZiBzaW1pbGFyIHVzZXJzLg0KDQogICAgLSAgIEl0IGFuYWx5emVzIHBhdHRlcm5zIGluIHVzZXIgcmF0aW5ncywgcHVyY2hhc2VzLCBvciBpbnRlcmFjdGlvbnMNCiAgICAgICAgdG8gaWRlbnRpZnkgdXNlcnMgd2l0aCBzaW1pbGFyIHRhc3Rlcy4NCg0KICAgIC0gICBUaGUgc3lzdGVtIHRoZW4gcmVjb21tZW5kcyBpdGVtcyB0aGF0IHNpbWlsYXIgdXNlcnMgaGF2ZSBsaWtlZA0KICAgICAgICBvciBpbnRlcmFjdGVkIHdpdGguDQoNCiAgICAtICAgS2V5IGFkdmFudGFnZXMgYXJlIHRoYXQgaXQgY2FuIG1ha2Ugc2VyZW5kaXBpdG91cw0KICAgICAgICByZWNvbW1lbmRhdGlvbnMgYW5kIGRvZXNuJ3QgcmVxdWlyZSBkZXRhaWxlZCBpdGVtIG1ldGFkYXRhLg0KDQogICAgLSAgIENoYWxsZW5nZXMgaW5jbHVkZSB0aGUgY29sZC1zdGFydCBwcm9ibGVtIChmb3IgbmV3IHVzZXJzL2l0ZW1zKQ0KICAgICAgICBhbmQgc3BhcnNpdHkgb2YgdXNlci1pdGVtIGludGVyYWN0aW9uIGRhdGEuDQoNCiAgICAqKkNvbnRlbnQtQmFzZWQgRmlsdGVyaW5nKioNCg0KICAgIC0gICBDb250ZW50LWJhc2VkIGZpbHRlcmluZyBtYWtlcyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gdGhlDQogICAgICAgIGF0dHJpYnV0ZXMgb3IgZmVhdHVyZXMgb2YgdGhlIGl0ZW1zIHRoZW1zZWx2ZXMuDQoNCiAgICAtICAgSXQgYW5hbHl6ZXMgdGhlIGNvbnRlbnQsIG1ldGFkYXRhLCBvciBkZXNjcmlwdGlvbnMgb2YgaXRlbXMgYQ0KICAgICAgICB1c2VyIGhhcyBsaWtlZCBpbiB0aGUgcGFzdC4NCg0KICAgIC0gICBUaGUgc3lzdGVtIHRoZW4gcmVjb21tZW5kcyBvdGhlciBpdGVtcyB3aXRoIHNpbWlsYXIgY29udGVudA0KICAgICAgICBjaGFyYWN0ZXJpc3RpY3MuDQoNCiAgICAtICAgS2V5IGFkdmFudGFnZXMgYXJlIHRoYXQgaXQgY2FuIGhhbmRsZSB0aGUgY29sZC1zdGFydCBwcm9ibGVtIGFuZA0KICAgICAgICBkb2Vzbid0IHJlbHkgc29sZWx5IG9uIHVzZXIgaW50ZXJhY3Rpb25zLg0KDQogICAgLSAgIENoYWxsZW5nZXMgaW5jbHVkZSB0aGUgbmVlZCBmb3IgcmljaCBpdGVtIG1ldGFkYXRhIGFuZCB0aGUNCiAgICAgICAgcG90ZW50aWFsIGZvciBvdmVyc3BlY2lhbGl6YXRpb24gKHJlY29tbWVuZGluZyB2ZXJ5IHNpbWlsYXINCiAgICAgICAgaXRlbXMpLg0KDQogICAgTWFueSBtb2Rlcm4gcmVjb21tZW5kZXIgc3lzdGVtcyB1c2UgYSBoeWJyaWQgYXBwcm9hY2gsIGNvbWJpbmluZw0KICAgIGNvbGxhYm9yYXRpdmUgYW5kIGNvbnRlbnQtYmFzZWQgZmlsdGVyaW5nIHRvIGxldmVyYWdlIHRoZSBzdHJlbmd0aHMNCiAgICBvZiBlYWNoIG1ldGhvZC4NCg0KLSAgIA0KDQogICAgMS4gICoqQ29sbGFib3JhdGl2ZSBGaWx0ZXJpbmcgUmVjb21tZW5kZXJzKio6DQogICAgICAgIC0gICBCYXNlZCBvbiB1c2VyLXVzZXIgb3IgaXRlbS1pdGVtIHNpbWlsYXJpdGllcw0KICAgICAgICAtICAgTWFrZSByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gdGhlIHByZWZlcmVuY2VzIGFuZCBiZWhhdmlvcnMNCiAgICAgICAgICAgIG9mIHNpbWlsYXIgdXNlcnMNCiAgICAgICAgLSAgIEV4YW1wbGVzOiBBbWF6b24ncyAiQ3VzdG9tZXJzIHdobyBib3VnaHQgdGhpcyBpdGVtIGFsc28NCiAgICAgICAgICAgIGJvdWdodC4uLiIsIE5ldGZsaXggbW92aWUgcmVjb21tZW5kYXRpb25zDQogICAgMi4gICoqQ29udGVudC1CYXNlZCBSZWNvbW1lbmRlcnMqKjoNCiAgICAgICAgLSAgIFJlY29tbWVuZCBpdGVtcyBzaW1pbGFyIHRvIHRoZSBvbmVzIGEgdXNlciBoYXMgbGlrZWQgaW4gdGhlDQogICAgICAgICAgICBwYXN0DQogICAgICAgIC0gICBBbmFseXplIHRoZSBjb250ZW50LCBtZXRhZGF0YSwgb3IgZGVzY3JpcHRpb25zIG9mIGl0ZW1zDQogICAgICAgIC0gICBFeGFtcGxlczogUmVjb21tZW5kaW5nIGJvb2tzIG9yIGFydGljbGVzIGJhc2VkIG9uIHRoZSB0b3BpY3MNCiAgICAgICAgICAgIG9yIGdlbnJlcyBhIHVzZXIgaGFzIHByZXZpb3VzbHkgZW5nYWdlZCB3aXRoDQogICAgMy4gICoqSHlicmlkIFJlY29tbWVuZGVycyoqOg0KICAgICAgICAtICAgQ29tYmluZSBjb2xsYWJvcmF0aXZlIGFuZCBjb250ZW50LWJhc2VkIGFwcHJvYWNoZXMNCiAgICAgICAgLSAgIENhbiBsZXZlcmFnZSB0aGUgc3RyZW5ndGhzIG9mIGVhY2ggdG8gb3ZlcmNvbWUgaW5kaXZpZHVhbA0KICAgICAgICAgICAgd2Vha25lc3Nlcw0KICAgICAgICAtICAgRXhhbXBsZXM6IENvbWJpbmluZyB1c2VyIHByZWZlcmVuY2VzIHdpdGggaXRlbSBmZWF0dXJlcyB0bw0KICAgICAgICAgICAgbWFrZSByZWNvbW1lbmRhdGlvbnMNCiAgICA0LiAgKipLbm93bGVkZ2UtQmFzZWQgUmVjb21tZW5kZXJzKio6DQogICAgICAgIC0gICBNYWtlIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiBleHBsaWNpdCBrbm93bGVkZ2UgYWJvdXQgdXNlcg0KICAgICAgICAgICAgcHJlZmVyZW5jZXMgYW5kIGl0ZW0gZmVhdHVyZXMNCiAgICAgICAgLSAgIFVzZSBydWxlLWJhc2VkIG9yIGNhc2UtYmFzZWQgcmVhc29uaW5nIHRvIG1hdGNoIHVzZXIgbmVlZHMNCiAgICAgICAgICAgIHdpdGggaXRlbSBhdHRyaWJ1dGVzDQogICAgICAgIC0gICBFeGFtcGxlczogUmVjb21tZW5kaW5nIHByb2R1Y3RzIGJhc2VkIG9uIHVzZXItc3BlY2lmaWVkDQogICAgICAgICAgICByZXF1aXJlbWVudHMNCiAgICA1LiAgKipEZW1vZ3JhcGhpYyBSZWNvbW1lbmRlcnMqKjoNCiAgICAgICAgLSAgIE1ha2UgcmVjb21tZW5kYXRpb25zIGJhc2VkIG9uIHVzZXIgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24NCiAgICAgICAgLSAgIEFzc3VtZSB1c2VycyB3aXRoIHNpbWlsYXIgZGVtb2dyYXBoaWMgcHJvZmlsZXMgaGF2ZSBzaW1pbGFyDQogICAgICAgICAgICBwcmVmZXJlbmNlcw0KICAgICAgICAtICAgRXhhbXBsZXM6IFJlY29tbWVuZGluZyBwcm9kdWN0cyBvciBjb250ZW50IGJhc2VkIG9uIGFnZSwNCiAgICAgICAgICAgIGdlbmRlciwgbG9jYXRpb24sIGV0Yy4NCiAgICA2LiAgKipDb250ZXh0LUF3YXJlIFJlY29tbWVuZGVycyoqOg0KICAgICAgICAtICAgVGFrZSBpbnRvIGFjY291bnQgdGhlIGN1cnJlbnQgY29udGV4dCAodGltZSwgbG9jYXRpb24sDQogICAgICAgICAgICBkZXZpY2UsIGV0Yy4pIHdoZW4gbWFraW5nIHJlY29tbWVuZGF0aW9ucw0KICAgICAgICAtICAgQWRqdXN0IHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB0aGUgdXNlcidzIHNpdHVhdGlvbiBhbmQNCiAgICAgICAgICAgIGVudmlyb25tZW50DQogICAgICAgIC0gICBFeGFtcGxlczogU3VnZ2VzdGluZyBuZWFyYnkgcmVzdGF1cmFudHMgb3IgZXZlbnRzIGJhc2VkIG9uDQogICAgICAgICAgICB0aGUgdXNlcidzIGN1cnJlbnQgbG9jYXRpb24NCiAgICAgICAgLSAgIA0KICAgICAgICAgICAgMS4gICoqRS1jb21tZXJjZSBhbmQgUmV0YWlsKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgcHJvZHVjdHMgb3Igc2VydmljZXMgYmFzZWQgb24gYSB1c2VyJ3MNCiAgICAgICAgICAgICAgICAgICAgYnJvd3NpbmcgYW5kIHB1cmNoYXNlIGhpc3RvcnkNCiAgICAgICAgICAgICAgICAtICAgUGVyc29uYWxpemluZyB0aGUgc2hvcHBpbmcgZXhwZXJpZW5jZSBhbmQgaW5jcmVhc2luZw0KICAgICAgICAgICAgICAgICAgICBzYWxlcw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogQW1hem9uJ3MgIkN1c3RvbWVycyB3aG8gYm91Z2h0IHRoaXMgYWxzbw0KICAgICAgICAgICAgICAgICAgICBib3VnaHQiIGFuZCBOZXRmbGl4J3MgbW92aWUgcmVjb21tZW5kYXRpb25zDQogICAgICAgICAgICAyLiAgKipNZWRpYSBhbmQgRW50ZXJ0YWlubWVudCoqOg0KICAgICAgICAgICAgICAgIC0gICBTdWdnZXN0aW5nIG1vdmllcywgVFYgc2hvd3MsIG11c2ljLCBib29rcywgb3INCiAgICAgICAgICAgICAgICAgICAgYXJ0aWNsZXMgYmFzZWQgb24gdXNlciBwcmVmZXJlbmNlcw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgY29udGVudCBkaXNjb3ZlcnkgYW5kIGVuZ2FnZW1lbnQNCiAgICAgICAgICAgICAgICAtICAgRXhhbXBsZXM6IFlvdVR1YmUncyB2aWRlbyByZWNvbW1lbmRhdGlvbnMgYW5kDQogICAgICAgICAgICAgICAgICAgIFNwb3RpZnkncyBtdXNpYyBzdWdnZXN0aW9ucw0KICAgICAgICAgICAgMy4gICoqU29jaWFsIE1lZGlhIGFuZCBDb250ZW50IFBsYXRmb3JtcyoqOg0KICAgICAgICAgICAgICAgIC0gICBSZWNvbW1lbmRpbmcgcG9zdHMsIGFjY291bnRzLCBvciBjb21tdW5pdGllcyBiYXNlZA0KICAgICAgICAgICAgICAgICAgICBvbiB1c2VyIGludGVyZXN0cyBhbmQgc29jaWFsIGNvbm5lY3Rpb25zDQogICAgICAgICAgICAgICAgLSAgIEluY3JlYXNpbmcgdXNlciBlbmdhZ2VtZW50IGFuZCB0aW1lIHNwZW50IG9uIHRoZQ0KICAgICAgICAgICAgICAgICAgICBwbGF0Zm9ybQ0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogRmFjZWJvb2sncyBuZXdzIGZlZWQgcmVjb21tZW5kYXRpb25zIGFuZA0KICAgICAgICAgICAgICAgICAgICBUd2l0dGVyJ3MgIldobyB0byBGb2xsb3ciIHN1Z2dlc3Rpb25zDQogICAgICAgICAgICA0LiAgKipKb2IgYW5kIFRhbGVudCBNYXRjaGluZyoqOg0KICAgICAgICAgICAgICAgIC0gICBNYXRjaGluZyBqb2Igc2Vla2VycyB3aXRoIHJlbGV2YW50IGpvYiBwb3N0aW5ncw0KICAgICAgICAgICAgICAgICAgICBiYXNlZCBvbiB0aGVpciBza2lsbHMgYW5kIGV4cGVyaWVuY2UNCiAgICAgICAgICAgICAgICAtICAgSGVscGluZyBlbXBsb3llcnMgZmluZCB0aGUgYmVzdCBjYW5kaWRhdGVzIGZvciBvcGVuDQogICAgICAgICAgICAgICAgICAgIHBvc2l0aW9ucw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogTGlua2VkSW4ncyBqb2IgcmVjb21tZW5kYXRpb25zIGFuZA0KICAgICAgICAgICAgICAgICAgICByZWNydWl0bWVudCBwbGF0Zm9ybXMnIGNhbmRpZGF0ZSBtYXRjaGluZw0KICAgICAgICAgICAgNS4gICoqRmluYW5jaWFsIFNlcnZpY2VzKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgaW52ZXN0bWVudCBvcHBvcnR1bml0aWVzLCBmaW5hbmNpYWwNCiAgICAgICAgICAgICAgICAgICAgcHJvZHVjdHMsIG9yIHNlcnZpY2VzIGJhc2VkIG9uIGEgdXNlcidzIGZpbmFuY2lhbA0KICAgICAgICAgICAgICAgICAgICBwcm9maWxlIGFuZCBnb2Fscw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgZmluYW5jaWFsIHBsYW5uaW5nIGFuZCBkZWNpc2lvbi1tYWtpbmcNCiAgICAgICAgICAgICAgICAtICAgRXhhbXBsZXM6IFJvYm8tYWR2aXNvcnMnIGludmVzdG1lbnQgcmVjb21tZW5kYXRpb25zDQogICAgICAgICAgICAgICAgICAgIGFuZCBiYW5raW5nIGFwcHMnIHByb2R1Y3Qgc3VnZ2VzdGlvbnMNCiAgICAgICAgICAgIDYuICAqKkhlYWx0aGNhcmUgYW5kIFdlbGxuZXNzKio6DQogICAgICAgICAgICAgICAgLSAgIFJlY29tbWVuZGluZyB0cmVhdG1lbnRzLCBtZWRpY2F0aW9ucywgb3IgbGlmZXN0eWxlDQogICAgICAgICAgICAgICAgICAgIGNoYW5nZXMgYmFzZWQgb24gYSBwYXRpZW50J3MgbWVkaWNhbCBoaXN0b3J5IGFuZA0KICAgICAgICAgICAgICAgICAgICBzeW1wdG9tcw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgcGVyc29uYWxpemVkIGhlYWx0aGNhcmUgYW5kIHByb21vdGluZw0KICAgICAgICAgICAgICAgICAgICBoZWFsdGh5IGJlaGF2aW9ycw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogVGVsZW1lZGljaW5lIHBsYXRmb3JtcycgdHJlYXRtZW50DQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyBhbmQgZml0bmVzcyBhcHBzJyB3b3Jrb3V0DQogICAgICAgICAgICAgICAgICAgIHN1Z2dlc3Rpb25zDQogICAgICAgICAgICA3LiAgKipFZHVjYXRpb24gYW5kIExlYXJuaW5nKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgY291cnNlcywgbGVhcm5pbmcgbWF0ZXJpYWxzLCBvcg0KICAgICAgICAgICAgICAgICAgICBlZHVjYXRpb25hbCByZXNvdXJjZXMgYmFzZWQgb24gYSBzdHVkZW50J3MgaW50ZXJlc3RzDQogICAgICAgICAgICAgICAgICAgIGFuZCBwZXJmb3JtYW5jZQ0KICAgICAgICAgICAgICAgIC0gICBFbmhhbmNpbmcgdGhlIGxlYXJuaW5nIGV4cGVyaWVuY2UgYW5kIHN1cHBvcnRpbmcNCiAgICAgICAgICAgICAgICAgICAgcGVyc29uYWxpemVkIGVkdWNhdGlvbg0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogT25saW5lIGxlYXJuaW5nIHBsYXRmb3JtcycgY291cnNlDQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyBhbmQgZWR1Y2F0aW9uYWwgYXBwcycgY29udGVudA0KICAgICAgICAgICAgICAgICAgICBzdWdnZXN0aW9ucw0KDQotICAgUmVjb21tZW5kZXIgc3lzdGVtcyBjYW4gYmUgcmVwcmVzZW50ZWQgbWF0aGVtYXRpY2FsbHkgdXNpbmcgdGhlDQogICAgZm9sbG93aW5nIGtleSBjb21wb25lbnRzOg0KDQogICAgMS4gICoqVXNlcnMqKjogTGV0IFUgPSB7dTEsIHUyLCAuLi4sIHVtfSBiZSB0aGUgc2V0IG9mIG0gdXNlcnMuDQoNCiAgICAyLiAgKipJdGVtcyoqOiBMZXQgSSA9IHtpMSwgaTIsIC4uLiwgaW59IGJlIHRoZSBzZXQgb2YgbiBpdGVtcy4NCg0KICAgIDMuICAqKlVzZXItSXRlbSBJbnRlcmFjdGlvbnMqKjogTGV0IFIgYmUgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbg0KICAgICAgICBtYXRyaXgsIHdoZXJlIFJbdSwgaV0gcmVwcmVzZW50cyB0aGUgcmF0aW5nLCBwcmVmZXJlbmNlLCBvcg0KICAgICAgICBpbnRlcmFjdGlvbiBvZiB1c2VyIHUgd2l0aCBpdGVtIGkuDQoNCiAgICAgICAgLSAgIFIgY2FuIGJlIGEgc3BhcnNlIG1hdHJpeCwgYXMgdXNlcnMgdHlwaWNhbGx5IGludGVyYWN0IHdpdGgNCiAgICAgICAgICAgIG9ubHkgYSBzbWFsbCBzdWJzZXQgb2YgYWxsIGF2YWlsYWJsZSBpdGVtcy4NCiAgICAgICAgLSAgIFIgY2FuIGNvbnRhaW4gZXhwbGljaXQgcmF0aW5ncyAoZS5nLiwgMS01IHN0YXJzKSBvciBpbXBsaWNpdA0KICAgICAgICAgICAgaW50ZXJhY3Rpb25zIChlLmcuLCBwdXJjaGFzZXMsIHZpZXdzLCBjbGlja3MpLg0KDQogICAgNC4gICoqUHJlZGljdGlvbiBGdW5jdGlvbioqOiBUaGUgZ29hbCBvZiBhIHJlY29tbWVuZGVyIHN5c3RlbSBpcyB0bw0KICAgICAgICBsZWFybiBhIHByZWRpY3Rpb24gZnVuY3Rpb24gZjogVSDDlyBJIOKGkiBSIHRoYXQgZXN0aW1hdGVzIHRoZQ0KICAgICAgICBwcmVmZXJlbmNlIG9yIHJhdGluZyBvZiBhIHVzZXIgdSBmb3IgYW4gaXRlbSBpLg0KDQogICAgICAgIC0gICBUaGlzIGZ1bmN0aW9uIGNhbiBiZSBsZWFybmVkIHVzaW5nIHZhcmlvdXMgbWFjaGluZSBsZWFybmluZw0KICAgICAgICAgICAgdGVjaG5pcXVlcywgc3VjaCBhcyBtYXRyaXggZmFjdG9yaXphdGlvbiwgZGVlcCBsZWFybmluZywgb3INCiAgICAgICAgICAgIGh5YnJpZCBhcHByb2FjaGVzLg0KDQogICAgNS4gICoqUmVjb21tZW5kYXRpb24gR2VuZXJhdGlvbioqOiBHaXZlbiBhIHVzZXIgdSwgdGhlIHJlY29tbWVuZGVyDQogICAgICAgIHN5c3RlbSBnZW5lcmF0ZXMgYSByYW5rZWQgbGlzdCBvZiBpdGVtcyBpIOKIiCBJIHRoYXQgdGhlIHVzZXIgaXMNCiAgICAgICAgbW9zdCBsaWtlbHkgdG8gaW50ZXJhY3Qgd2l0aCBvciBwcmVmZXIsIGJhc2VkIG9uIHRoZSBsZWFybmVkDQogICAgICAgIHByZWRpY3Rpb24gZnVuY3Rpb24gZi4NCg0KICAgIDYuICAqKkV2YWx1YXRpb24gTWV0cmljcyoqOiBSZWNvbW1lbmRlciBzeXN0ZW1zIGFyZSB0eXBpY2FsbHkNCiAgICAgICAgZXZhbHVhdGVkIHVzaW5nIG1ldHJpY3Mgc3VjaCBhczoNCg0KICAgICAgICAtICAgW1ByZWNpc2lvblxAa10obWFpbHRvOlByZWNpc2lvbkBrKXsuZW1haWx9OiBUaGUgZnJhY3Rpb24gb2YNCiAgICAgICAgICAgIHRoZSB0b3AtayByZWNvbW1lbmRlZCBpdGVtcyB0aGF0IGFyZSByZWxldmFudCB0byB0aGUgdXNlci4NCiAgICAgICAgLSAgIFtSZWNhbGxcQGtdKG1haWx0bzpSZWNhbGxAayl7LmVtYWlsfTogVGhlIGZyYWN0aW9uIG9mDQogICAgICAgICAgICByZWxldmFudCBpdGVtcyB0aGF0IGFyZSBpbmNsdWRlZCBpbiB0aGUgdG9wLWsNCiAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucy4NCiAgICAgICAgLSAgIE5vcm1hbGl6ZWQgRGlzY291bnRlZCBDdW11bGF0aXZlIEdhaW4gKE5EQ0cpOiBBIG1lYXN1cmUgb2YNCiAgICAgICAgICAgIHJhbmtpbmcgcXVhbGl0eSB0aGF0IGNvbnNpZGVycyB0aGUgcG9zaXRpb24gb2YgcmVsZXZhbnQNCiAgICAgICAgICAgIGl0ZW1zIGluIHRoZSByZWNvbW1lbmRhdGlvbiBsaXN0Lg0KICAgICAgICAtICAgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIG9yIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKToNCiAgICAgICAgICAgIE1lYXN1cmVzIHRoZSBhY2N1cmFjeSBvZiByYXRpbmcgcHJlZGljdGlvbnMuDQoNCiAgICBUaGlzIG1hdGhlbWF0aWNhbCByZXByZXNlbnRhdGlvbiBwcm92aWRlcyBhIGZyYW1ld29yayBmb3INCiAgICB1bmRlcnN0YW5kaW5nIHRoZSBjb3JlIGNvbXBvbmVudHMgYW5kIG9iamVjdGl2ZXMgb2YgcmVjb21tZW5kZXINCiAgICBzeXN0ZW1zLCB3aGljaCBjYW4gdGhlbiBiZSBpbXBsZW1lbnRlZCB1c2luZyB2YXJpb3VzIGFsZ29yaXRobXMgYW5kDQogICAgdGVjaG5pcXVlcy4NCg0KLSAgIA0KDQogICAgIyMgUmVjb21tZW5kZXIgU3lzdGVtczogTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uDQoNCiAgICAxLiAgKipVc2VycyBhbmQgSXRlbXMqKjoNCiAgICAgICAgLSAgIExldCBVID0ge3UxLCB1MiwgLi4uLCB1bX0gYmUgdGhlIHNldCBvZiBtIHVzZXJzLg0KICAgICAgICAtICAgTGV0IEkgPSB7aTEsIGkyLCAuLi4sIGlufSBiZSB0aGUgc2V0IG9mIG4gaXRlbXMuDQogICAgMi4gICoqVXNlci1JdGVtIEludGVyYWN0aW9ucyoqOg0KICAgICAgICAtICAgTGV0IFIgYmUgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbiBtYXRyaXgsIHdoZXJlIFJbdSwgaV0NCiAgICAgICAgICAgIHJlcHJlc2VudHMgdGhlIHJhdGluZywgcHJlZmVyZW5jZSwgb3IgaW50ZXJhY3Rpb24gb2YgdXNlciB1DQogICAgICAgICAgICB3aXRoIGl0ZW0gaS4NCiAgICAgICAgLSAgIFIgaXMgdHlwaWNhbGx5IGEgc3BhcnNlIG1hdHJpeCwgYXMgdXNlcnMgaW50ZXJhY3Qgd2l0aCBvbmx5DQogICAgICAgICAgICBhIHNtYWxsIHN1YnNldCBvZiBhbGwgYXZhaWxhYmxlIGl0ZW1zLg0KICAgICAgICAtICAgUiBjYW4gY29udGFpbiBleHBsaWNpdCByYXRpbmdzIChlLmcuLCAxLTUgc3RhcnMpIG9yIGltcGxpY2l0DQogICAgICAgICAgICBpbnRlcmFjdGlvbnMgKGUuZy4sIHB1cmNoYXNlcywgdmlld3MsIGNsaWNrcykuDQogICAgMy4gICoqUHJlZGljdGlvbiBGdW5jdGlvbioqOg0KICAgICAgICAtICAgVGhlIGdvYWwgaXMgdG8gbGVhcm4gYSBwcmVkaWN0aW9uIGZ1bmN0aW9uIGY6IFUgw5cgSSDihpIgUiB0aGF0DQogICAgICAgICAgICBlc3RpbWF0ZXMgdGhlIHByZWZlcmVuY2Ugb3IgcmF0aW5nIG9mIGEgdXNlciB1IGZvciBhbiBpdGVtDQogICAgICAgICAgICBpLg0KICAgICAgICAtICAgVGhpcyBmdW5jdGlvbiBjYW4gYmUgbGVhcm5lZCB1c2luZyB2YXJpb3VzIG1hY2hpbmUgbGVhcm5pbmcNCiAgICAgICAgICAgIHRlY2huaXF1ZXMsIHN1Y2ggYXMgbWF0cml4IGZhY3Rvcml6YXRpb24sIGRlZXAgbGVhcm5pbmcsIG9yDQogICAgICAgICAgICBoeWJyaWQgYXBwcm9hY2hlcy4NCiAgICA0LiAgKipSZWNvbW1lbmRhdGlvbiBHZW5lcmF0aW9uKio6DQogICAgICAgIC0gICBHaXZlbiBhIHVzZXIgdSwgdGhlIHJlY29tbWVuZGVyIHN5c3RlbSBnZW5lcmF0ZXMgYSByYW5rZWQNCiAgICAgICAgICAgIGxpc3Qgb2YgaXRlbXMgaSDiiIggSSB0aGF0IHRoZSB1c2VyIGlzIG1vc3QgbGlrZWx5IHRvIGludGVyYWN0DQogICAgICAgICAgICB3aXRoIG9yIHByZWZlciwgYmFzZWQgb24gdGhlIGxlYXJuZWQgcHJlZGljdGlvbiBmdW5jdGlvbiBmLg0KICAgIDUuICAqKkV2YWx1YXRpb24gTWV0cmljcyoqOg0KICAgICAgICAtICAgW1ByZWNpc2lvblxAa10obWFpbHRvOlByZWNpc2lvbkBrKXsuZW1haWx9LA0KICAgICAgICAgICAgW1JlY2FsbFxAa10obWFpbHRvOlJlY2FsbEBrKXsuZW1haWx9LCBOb3JtYWxpemVkIERpc2NvdW50ZWQNCiAgICAgICAgICAgIEN1bXVsYXRpdmUgR2FpbiAoTkRDRyksIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSwgUm9vdCBNZWFuDQogICAgICAgICAgICBTcXVhcmVkIEVycm9yIChSTVNFKS4NCg0KICAgICMjIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaW4gTWFjaGluZSBMZWFybmluZw0KDQogICAgU1ZEIGlzIGEgcG93ZXJmdWwgbWF0cml4IGZhY3Rvcml6YXRpb24gdGVjaG5pcXVlIHRoYXQgY2FuIGJlIHVzZWQNCiAgICBmb3IgdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIHRhc2tzLCBpbmNsdWRpbmcgcmVjb21tZW5kZXIgc3lzdGVtcy4NCg0KICAgICoqRXhhbXBsZTogU1ZEIGZvciBDb2xsYWJvcmF0aXZlIEZpbHRlcmluZyBpbiBSZWNvbW1lbmRlciBTeXN0ZW1zKioNCg0KICAgIDEuICAqKlVzZXItSXRlbSBJbnRlcmFjdGlvbiBNYXRyaXgqKjoNCiAgICAgICAgLSAgIExldCBSIGJlIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4LCB3aGVyZSBSW3UsIGldDQogICAgICAgICAgICByZXByZXNlbnRzIHRoZSByYXRpbmcgb3IgcHJlZmVyZW5jZSBvZiB1c2VyIHUgZm9yIGl0ZW0gaS4NCiAgICAyLiAgKipTVkQgRGVjb21wb3NpdGlvbioqOg0KICAgICAgICAtICAgRGVjb21wb3NlIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4IFIgaW50byB0aHJlZQ0KICAgICAgICAgICAgbWF0cmljZXM6IFUsIM6jLCBhbmQgVlxeVC4NCiAgICAgICAgLSAgIFIgPSBVzqNWXF5ULCB3aGVyZToNCiAgICAgICAgICAgIC0gICBVIGlzIGFuIG0gw5cgbSBvcnRob2dvbmFsIG1hdHJpeCByZXByZXNlbnRpbmcgdGhlIGxlZnQNCiAgICAgICAgICAgICAgICBzaW5ndWxhciB2ZWN0b3JzLg0KICAgICAgICAgICAgLSAgIM6jIGlzIGFuIG0gw5cgbiBkaWFnb25hbCBtYXRyaXggY29udGFpbmluZyB0aGUgc2luZ3VsYXINCiAgICAgICAgICAgICAgICB2YWx1ZXMuDQogICAgICAgICAgICAtICAgVlxeVCBpcyBhbiBuIMOXIG4gb3J0aG9nb25hbCBtYXRyaXggcmVwcmVzZW50aW5nIHRoZQ0KICAgICAgICAgICAgICAgIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMuDQogICAgMy4gICoqUmVjb21tZW5kYXRpb24gR2VuZXJhdGlvbioqOg0KICAgICAgICAtICAgVG8gcHJlZGljdCB0aGUgcmF0aW5nIG9yIHByZWZlcmVuY2Ugb2YgYSB1c2VyIHUgZm9yIGFuIGl0ZW0NCiAgICAgICAgICAgIGksIHVzZSB0aGUgZm9sbG93aW5nIGZvcm11bGE6DQogICAgICAgICAgICAtICAgUlt1LCBpXSDiiYggKFUgzqMgVlxeVClbdSwgaV0NCiAgICAgICAgLSAgIFRoZSB0b3AtayBpdGVtcyB3aXRoIHRoZSBoaWdoZXN0IHByZWRpY3RlZCByYXRpbmdzIGNhbiBiZQ0KICAgICAgICAgICAgcmVjb21tZW5kZWQgdG8gdGhlIHVzZXIuDQogICAgNC4gICoqQWR2YW50YWdlcyBvZiBTVkQqKjoNCiAgICAgICAgLSAgIEhhbmRsZXMgdGhlIHNwYXJzaXR5IG9mIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4Lg0KICAgICAgICAtICAgQ2FwdHVyZXMgdGhlIGxhdGVudCBmYWN0b3JzIG9yIGhpZGRlbiBmZWF0dXJlcyB0aGF0DQogICAgICAgICAgICBpbmZsdWVuY2UgdXNlciBwcmVmZXJlbmNlcy4NCiAgICAgICAgLSAgIFByb3ZpZGVzIGEgbG93LXJhbmsgYXBwcm94aW1hdGlvbiBvZiB0aGUgb3JpZ2luYWwgbWF0cml4LA0KICAgICAgICAgICAgd2hpY2ggY2FuIGltcHJvdmUgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5Lg0KICAgICAgICAtICAgQ2FuIGJlIGNvbWJpbmVkIHdpdGggb3RoZXIgdGVjaG5pcXVlcywgc3VjaCBhcw0KICAgICAgICAgICAgcmVndWxhcml6YXRpb24sIHRvIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZQ0KICAgICAgICAgICAgcmVjb21tZW5kZXIgc3lzdGVtLg0KDQogICAgKipFeGFtcGxlOiBTVkQgZm9yIEltYWdlIENvbXByZXNzaW9uKioNCg0KICAgIDEuICAqKkltYWdlIFJlcHJlc2VudGF0aW9uKio6DQogICAgICAgIC0gICBMZXQgWCBiZSB0aGUgbSDDlyBuIGltYWdlIG1hdHJpeCwgd2hlcmUgZWFjaCBlbGVtZW50DQogICAgICAgICAgICByZXByZXNlbnRzIHRoZSBwaXhlbCBpbnRlbnNpdHkuDQogICAgMi4gICoqU1ZEIERlY29tcG9zaXRpb24qKjoNCiAgICAgICAgLSAgIERlY29tcG9zZSB0aGUgaW1hZ2UgbWF0cml4IFggaW50byB0aHJlZSBtYXRyaWNlczogVSwgzqMsIGFuZA0KICAgICAgICAgICAgVlxeVC4NCiAgICAgICAgLSAgIFggPSBVzqNWXF5ULCB3aGVyZToNCiAgICAgICAgICAgIC0gICBVIGlzIGFuIG0gw5cgbSBvcnRob2dvbmFsIG1hdHJpeCByZXByZXNlbnRpbmcgdGhlIGxlZnQNCiAgICAgICAgICAgICAgICBzaW5ndWxhciB2ZWN0b3JzLg0KICAgICAgICAgICAgLSAgIM6jIGlzIGFuIG0gw5cgbiBkaWFnb25hbCBtYXRyaXggY29udGFpbmluZyB0aGUgc2luZ3VsYXINCiAgICAgICAgICAgICAgICB2YWx1ZXMuDQogICAgICAgICAgICAtICAgVlxeVCBpcyBhbiBuIMOXIG4gb3J0aG9nb25hbCBtYXRyaXggcmVwcmVzZW50aW5nIHRoZQ0KICAgICAgICAgICAgICAgIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMuDQogICAgMy4gICoqSW1hZ2UgQ29tcHJlc3Npb24qKjoNCiAgICAgICAgLSAgIFJldGFpbiBvbmx5IHRoZSBrIGxhcmdlc3Qgc2luZ3VsYXIgdmFsdWVzIGluIM6jIGFuZCB0aGUNCiAgICAgICAgICAgIGNvcnJlc3BvbmRpbmcgY29sdW1ucyBpbiBVIGFuZCBWXF5ULg0KICAgICAgICAtICAgVGhlIGNvbXByZXNzZWQgaW1hZ2UgY2FuIGJlIHJlY29uc3RydWN0ZWQgYXMgWF9jb21wcmVzc2VkID0NCiAgICAgICAgICAgIFVfayDOo19rIFZfa1xeVCwgd2hlcmUgdGhlIHN1YnNjcmlwdCBrIGluZGljYXRlcyB0aGUNCiAgICAgICAgICAgIHJlZHVjZWQtcmFuayBtYXRyaWNlcy4NCiAgICA0LiAgKipBZHZhbnRhZ2VzIG9mIFNWRCBmb3IgSW1hZ2UgQ29tcHJlc3Npb24qKjoNCiAgICAgICAgLSAgIFByb3ZpZGVzIGEgbG93LXJhbmsgYXBwcm94aW1hdGlvbiBvZiB0aGUgb3JpZ2luYWwgaW1hZ2UsDQogICAgICAgICAgICByZWR1Y2luZyB0aGUgc3RvcmFnZSBhbmQgdHJhbnNtaXNzaW9uIHJlcXVpcmVtZW50cy4NCiAgICAgICAgLSAgIFByZXNlcnZlcyB0aGUgbW9zdCBpbXBvcnRhbnQgZmVhdHVyZXMgYW5kIHN0cnVjdHVyZXMgb2YgdGhlDQogICAgICAgICAgICBpbWFnZSwgcmVzdWx0aW5nIGluIGhpZ2gtcXVhbGl0eSByZWNvbnN0cnVjdGlvbnMuDQogICAgICAgIC0gICBDYW4gYmUgdXNlZCBmb3IgdmFyaW91cyBpbWFnZSBwcm9jZXNzaW5nIHRhc2tzLCBzdWNoIGFzDQogICAgICAgICAgICBkZW5vaXNpbmcsIGZlYXR1cmUgZXh0cmFjdGlvbiwgYW5kIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4NCg0KICAgIFNWRCBpcyBhIHZlcnNhdGlsZSB0ZWNobmlxdWUgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBhIHdpZGUgcmFuZ2Ugb2YNCiAgICBtYWNoaW5lIGxlYXJuaW5nIHByb2JsZW1zLCBpbmNsdWRpbmcgcmVjb21tZW5kZXIgc3lzdGVtcywgaW1hZ2UNCiAgICBwcm9jZXNzaW5nLCBhbmQgZGF0YSBhbmFseXNpcy4gVW5kZXJzdGFuZGluZyB0aGUgbWF0aGVtYXRpY2FsDQogICAgcmVwcmVzZW50YXRpb24gYW5kIGV4YW1wbGVzIG9mIFNWRCBpcyBjcnVjaWFsIGZvciBkZXZlbG9waW5nDQogICAgZWZmZWN0aXZlIGFuZCBlZmZpY2llbnQgbWFjaGluZSBsZWFybmluZyBzb2x1dGlvbnMuDQoNCi0gICANCg0KICAgICMgU3R1ZHkgR3VpZGU6IFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaW4gTWFjaGluZSBMZWFybmluZw0KDQogICAgIyMgSW50cm9kdWN0aW9uIHRvIFNWRA0KDQogICAgU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSBpcyBhIG1hdHJpeCBmYWN0b3JpemF0aW9uDQogICAgdGVjaG5pcXVlIHVzZWQgaW4gdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIGFwcGxpY2F0aW9ucywgaW5jbHVkaW5nDQogICAgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLCBub2lzZSByZWR1Y3Rpb24sIGFuZCBjb2xsYWJvcmF0aXZlDQogICAgZmlsdGVyaW5nIGluIHJlY29tbWVuZGVyIHN5c3RlbXMuDQoNCiAgICAjIyMgTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uDQoNCiAgICBHaXZlbiBhIG1hdHJpeCAkQSQgb2Ygc2l6ZSAkbSBcdGltZXMgbiQsIFNWRCBkZWNvbXBvc2VzICRBJCBpbnRvDQogICAgdGhyZWUgbWF0cmljZXM6DQoNCiAgICAkJCBBID0gVSBcU2lnbWEgVl5UICQkDQoNCiAgICAtICAgJFUkIGlzIGFuICRtIFx0aW1lcyBtJCBvcnRob2dvbmFsIG1hdHJpeC4gVGhlIGNvbHVtbnMgb2YgJFUkIGFyZQ0KICAgICAgICB0aGUgbGVmdCBzaW5ndWxhciB2ZWN0b3JzIG9mICRBJC4NCiAgICAtICAgJFxTaWdtYSQgaXMgYW4gJG0gXHRpbWVzIG4kIGRpYWdvbmFsIG1hdHJpeCB3aXRoIG5vbi1uZWdhdGl2ZQ0KICAgICAgICByZWFsIG51bWJlcnMgb24gdGhlIGRpYWdvbmFsLiBUaGVzZSBudW1iZXJzIGFyZSB0aGUgc2luZ3VsYXINCiAgICAgICAgdmFsdWVzIG9mICRBJC4NCiAgICAtICAgJFZeVCQgaXMgdGhlIHRyYW5zcG9zZSBvZiBhbiAkbiBcdGltZXMgbiQgb3J0aG9nb25hbCBtYXRyaXguIFRoZQ0KICAgICAgICBjb2x1bW5zIG9mICRWJCBhcmUgdGhlIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMgb2YgJEEkLg0KDQogICAgIyMjIFByb3BlcnRpZXMNCg0KICAgIC0gICBUaGUgc2luZ3VsYXIgdmFsdWVzIGluICRcU2lnbWEkIGFyZSBzb3J0ZWQgaW4gZGVzY2VuZGluZyBvcmRlci4NCiAgICAtICAgVGhlIG51bWJlciBvZiBub24temVybyBzaW5ndWxhciB2YWx1ZXMgaXMgZXF1YWwgdG8gdGhlIHJhbmsgb2YNCiAgICAgICAgdGhlIG1hdHJpeCAkQSQuDQoNCiAgICAjIyBBcHBsaWNhdGlvbnMgb2YgU1ZEIGluIE1hY2hpbmUgTGVhcm5pbmcNCg0KICAgICMjIyAxLiBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24NCg0KICAgIC0gICAqKlByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkqKjogU1ZEIGlzIHVzZWQgdG8gY29tcHV0ZQ0KICAgICAgICB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgYSBkYXRhc2V0LCB3aGljaCBhcmUgdGhlIGRpcmVjdGlvbnMNCiAgICAgICAgb2YgbWF4aW11bSB2YXJpYW5jZS4gQnkgcHJvamVjdGluZyBkYXRhIG9udG8gdGhlIGZpcnN0IGZldw0KICAgICAgICBwcmluY2lwYWwgY29tcG9uZW50cywgd2UgY2FuIHJlZHVjZSB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlDQogICAgICAgIGRhdGEgd2hpbGUgcHJlc2VydmluZyBtb3N0IG9mIGl0cyB2YXJpYW5jZS4NCg0KICAgICMjIyAyLiBSZWNvbW1lbmRlciBTeXN0ZW1zDQoNCiAgICAtICAgKipDb2xsYWJvcmF0aXZlIEZpbHRlcmluZyoqOiBJbiByZWNvbW1lbmRlciBzeXN0ZW1zLCBTVkQgaXMgdXNlZA0KICAgICAgICB0byBkZWNvbXBvc2UgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbiBtYXRyaXggaW50byBsYXRlbnQNCiAgICAgICAgZmFjdG9ycy4gVGhpcyBoZWxwcyBpbiBwcmVkaWN0aW5nIG1pc3NpbmcgZW50cmllcyAoZS5nLiwNCiAgICAgICAgcmF0aW5ncykgYnkgcmVjb25zdHJ1Y3RpbmcgdGhlIG1hdHJpeCB1c2luZyBhIHJlZHVjZWQgbnVtYmVyIG9mDQogICAgICAgIHNpbmd1bGFyIHZhbHVlcy4NCg0KICAgICMjIyMgRXhhbXBsZTogTW92aWUgUmVjb21tZW5kYXRpb24NCg0KICAgIDEuICAqKlVzZXItSXRlbSBNYXRyaXgqKjogQ29uc2lkZXIgYSBtYXRyaXggJFIkIHdoZXJlIHJvd3MgcmVwcmVzZW50DQogICAgICAgIHVzZXJzIGFuZCBjb2x1bW5zIHJlcHJlc2VudCBtb3ZpZXMuIEVhY2ggZW50cnkgJFJbdSwgaV0kIGlzIHRoZQ0KICAgICAgICByYXRpbmcgZ2l2ZW4gYnkgdXNlciAkdSQgdG8gbW92aWUgJGkkLg0KDQogICAgMi4gICoqU1ZEIERlY29tcG9zaXRpb24qKjogRGVjb21wb3NlICRSJCB1c2luZyBTVkQ6DQoNCiAgICAgICAgJCQgUiBcYXBwcm94IFVfayBcU2lnbWFfayBWX2teVCAkJA0KDQogICAgICAgIEhlcmUsICRVX2skLCAkXFNpZ21hX2skLCBhbmQgJFZfa15UJCBhcmUgdHJ1bmNhdGVkIG1hdHJpY2VzDQogICAgICAgIGNvbnRhaW5pbmcgb25seSB0aGUgdG9wICRrJCBzaW5ndWxhciB2YWx1ZXMgYW5kIGNvcnJlc3BvbmRpbmcNCiAgICAgICAgdmVjdG9ycy4NCg0KICAgIDMuICAqKlByZWRpY3Rpb24qKjogUHJlZGljdCB0aGUgcmF0aW5nIGZvciBhIHVzZXItbW92aWUgcGFpciBieQ0KICAgICAgICByZWNvbnN0cnVjdGluZyB0aGUgbWF0cml4Og0KDQogICAgICAgICQkIFxoYXR7Un0gPSBVX2sgXFNpZ21hX2sgVl9rXlQgJCQNCg0KICAgICAgICBUaGUgcHJlZGljdGVkIHJhdGluZyBmb3IgdXNlciAkdSQgYW5kIG1vdmllICRpJCBpcw0KICAgICAgICAkXGhhdHtSfVt1LCBpXSQuDQoNCiAgICAjIyMgMy4gTm9pc2UgUmVkdWN0aW9uDQoNCiAgICAtICAgKipJbWFnZSBDb21wcmVzc2lvbioqOiBTVkQgY2FuIGJlIHVzZWQgdG8gY29tcHJlc3MgaW1hZ2VzIGJ5DQogICAgICAgIGtlZXBpbmcgb25seSB0aGUgbGFyZ2VzdCBzaW5ndWxhciB2YWx1ZXMsIHdoaWNoIGNhcHR1cmUgdGhlIG1vc3QNCiAgICAgICAgc2lnbmlmaWNhbnQgZmVhdHVyZXMgb2YgdGhlIGltYWdlLCB3aGlsZSBkaXNjYXJkaW5nIHNtYWxsZXINCiAgICAgICAgc2luZ3VsYXIgdmFsdWVzIHRoYXQgcmVwcmVzZW50IG5vaXNlLg0KDQogICAgIyMgUHJhY3RpY2FsIENvbnNpZGVyYXRpb25zDQoNCiAgICAtICAgKipDaG9vc2luZyoqICRrJDogVGhlIGNob2ljZSBvZiAkayQgKG51bWJlciBvZiBzaW5ndWxhciB2YWx1ZXMNCiAgICAgICAgdG8ga2VlcCkgaXMgY3J1Y2lhbC4gQSBzbWFsbGVyICRrJCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5IGJ1dA0KICAgICAgICBtYXkgbG9zZSBpbXBvcnRhbnQgaW5mb3JtYXRpb24sIHdoaWxlIGEgbGFyZ2VyICRrJCByZXRhaW5zIG1vcmUNCiAgICAgICAgaW5mb3JtYXRpb24gYnV0IG1heSBpbmNsdWRlIG5vaXNlLg0KICAgIC0gICAqKkNvbXB1dGF0aW9uYWwgQ29tcGxleGl0eSoqOiBTVkQgY2FuIGJlIGNvbXB1dGF0aW9uYWxseQ0KICAgICAgICBleHBlbnNpdmUgZm9yIGxhcmdlIG1hdHJpY2VzLiBFZmZpY2llbnQgYWxnb3JpdGhtcyBhbmQNCiAgICAgICAgYXBwcm94aW1hdGlvbnMgKGUuZy4sIHRydW5jYXRlZCBTVkQpIGFyZSBvZnRlbiB1c2VkIGluIHByYWN0aWNlLg0KDQogICAgIyMgQ29uY2x1c2lvbg0KDQogICAgU1ZEIGlzIGEgcG93ZXJmdWwgdG9vbCBpbiBtYWNoaW5lIGxlYXJuaW5nIGZvciB0YXNrcyBpbnZvbHZpbmcNCiAgICBtYXRyaXggZmFjdG9yaXphdGlvbi4gSXRzIGFiaWxpdHkgdG8gZGVjb21wb3NlIG1hdHJpY2VzIGludG8NCiAgICBtZWFuaW5nZnVsIGNvbXBvbmVudHMgbWFrZXMgaXQgaW52YWx1YWJsZSBmb3IgYXBwbGljYXRpb25zIGxpa2UNCiAgICBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24sIGNvbGxhYm9yYXRpdmUgZmlsdGVyaW5nLCBhbmQgbm9pc2UNCiAgICByZWR1Y3Rpb24uDQoNCi0gICANCg0KICAgICMgTWFjaGluZSBMZWFybmluZyBSZWNvbW1lbmRhdGlvbiBTeXN0ZW1zOiBTdHVkeSBHdWlkZQ0KDQogICAgIyMgSS4gQ29yZSBSZWNvbW1lbmRhdGlvbiBTeXN0ZW0gVHlwZXMNCg0KICAgICMjIyBBLiBDb2xsYWJvcmF0aXZlIEZpbHRlcmluZw0KDQogICAgMS4gICoqVXNlci1CYXNlZCAoVUJDRikqKg0KICAgICAgICAtICAgTWF0aGVtYXRpY2FsIHJlcHJlc2VudGF0aW9uOg0KDQogICAgICAgICAgICBgYGAgICAgICAgICANCiAgICAgICAgICAgIHByZWQodSxpKSA9IG1lYW4ocmF0aW5nc191KSArIM6jKHNpbSh1LHYpIMOXIChyYXRpbmdfdixpIC0gbWVhbihyYXRpbmdzX3YpKSkNCiAgICAgICAgICAgIHdoZXJlOg0KICAgICAgICAgICAgLSBwcmVkKHUsaSkgaXMgdGhlIHByZWRpY3Rpb24gZm9yIHVzZXIgdSBvbiBpdGVtIGkNCiAgICAgICAgICAgIC0gc2ltKHUsdikgaXMgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB1c2VycyB1IGFuZCB2DQogICAgICAgICAgICBgYGANCiAgICAyLiAgKipJdGVtLUJhc2VkIChJQkNGKSoqDQogICAgICAgIC0gICBNYXRoZW1hdGljYWwgcmVwcmVzZW50YXRpb246DQoNCiAgICAgICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICAgICAgcHJlZCh1LGkpID0gzqMoc2ltKGksaikgw5cgcmF0aW5nX3UsaikgLyDOo3xzaW0oaSxqKXwNCiAgICAgICAgICAgIHdoZXJlOg0KICAgICAgICAgICAgLSBzaW0oaSxqKSBpcyB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIGl0ZW1zIGkgYW5kIGoNCiAgICAgICAgICAgIGBgYA0KICAgIDMuICAqKk1vZGVsLUJhc2VkKioNCiAgICAgICAgLSAgIFVzZXMgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgdG8gcHJlZGljdCByYXRpbmdzDQoNCiAgICAgICAgLSAgIENvbW1vbiBhcHByb2FjaDogTWF0cml4IEZhY3Rvcml6YXRpb24NCg0KICAgICAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgICAgICBSIOKJiCBQIMOXIFFeVA0KICAgICAgICAgICAgd2hlcmU6DQogICAgICAgICAgICAtIFIgaXMgdGhlIHVzZXItaXRlbSByYXRpbmcgbWF0cml4DQogICAgICAgICAgICAtIFAgaXMgdGhlIHVzZXIgbGF0ZW50IGZhY3RvciBtYXRyaXgNCiAgICAgICAgICAgIC0gUSBpcyB0aGUgaXRlbSBsYXRlbnQgZmFjdG9yIG1hdHJpeA0KICAgICAgICAgICAgYGBgDQoNCiAgICAjIyMgQi4gQ29udGVudC1CYXNlZCBGaWx0ZXJpbmcNCg0KICAgIDEuICAqKkZlYXR1cmUgRXh0cmFjdGlvbioqDQogICAgICAgIC0gICBUZXh0OiBURi1JREYgcmVwcmVzZW50YXRpb24NCg0KICAgICAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgICAgICBURi1JREYodCxkKSA9IFRGKHQsZCkgw5cgSURGKHQpDQogICAgICAgICAgICB3aGVyZToNCiAgICAgICAgICAgIC0gVEYodCxkKSBpcyB0ZXJtIGZyZXF1ZW5jeQ0KICAgICAgICAgICAgLSBJREYodCkgaXMgaW52ZXJzZSBkb2N1bWVudCBmcmVxdWVuY3kNCiAgICAgICAgICAgIGBgYA0KDQogICAgICAgIC0gICBJbWFnZXM6IENOTiBmZWF0dXJlcw0KICAgIDIuICAqKlByb2ZpbGUgTGVhcm5pbmcqKg0KICAgICAgICAtICAgTWV0aG9kczoNCiAgICAgICAgICAgIC0gICBEZWNpc2lvbiBUcmVlcw0KICAgICAgICAgICAgLSAgIE5haXZlIEJheWVzDQogICAgICAgICAgICAtICAgTmV1cmFsIE5ldHdvcmtzDQogICAgICAgICAgICAtICAgU1ZNDQogICAgICAgICAgICAtICAgUmVncmVzc2lvbiBNb2RlbHMNCg0KICAgICMjIElJLiBBZHZhbmNlZCBUZWNobmlxdWVzDQoNCiAgICAjIyMgQS4gTWF0cml4IEZhY3Rvcml6YXRpb24NCg0KICAgIDEuICAqKlNWRCAoU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbikqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBBID0gVSDOoyBWXlQNCiAgICAgICAgd2hlcmU6DQogICAgICAgIC0gQSBpcyB0aGUgb3JpZ2luYWwgbWF0cml4DQogICAgICAgIC0gVSBjb250YWlucyBsZWZ0IHNpbmd1bGFyIHZlY3RvcnMNCiAgICAgICAgLSDOoyBjb250YWlucyBzaW5ndWxhciB2YWx1ZXMNCiAgICAgICAgLSBWXlQgY29udGFpbnMgcmlnaHQgc2luZ3VsYXIgdmVjdG9ycw0KICAgICAgICBgYGANCg0KICAgIDIuICAqKkFMUyAoQWx0ZXJuYXRpbmcgTGVhc3QgU3F1YXJlcykqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBNaW5pbWl6ZTogzqMocl91aSAtIHBfdV5UIHFfaSleMiArIM67KHx8cF91fHxeMiArIHx8cV9pfHxeMikNCiAgICAgICAgd2hlcmU6DQogICAgICAgIC0gcl91aSBpcyB0aGUgcmF0aW5nIG9mIHVzZXIgdSBmb3IgaXRlbSBpDQogICAgICAgIC0gcF91IGlzIHRoZSB1c2VyIGxhdGVudCBmYWN0b3INCiAgICAgICAgLSBxX2kgaXMgdGhlIGl0ZW0gbGF0ZW50IGZhY3Rvcg0KICAgICAgICAtIM67IGlzIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXINCiAgICAgICAgYGBgDQoNCiAgICAjIyMgQi4gRGVlcCBMZWFybmluZyBBcHByb2FjaGVzDQoNCiAgICAxLiAgTmV1cmFsIENvbGxhYm9yYXRpdmUgRmlsdGVyaW5nDQogICAgMi4gIEF1dG9lbmNvZGVycw0KICAgIDMuICBSTk5zIGZvciBTZXF1ZW50aWFsIFJlY29tbWVuZGF0aW9ucw0KICAgIDQuICBDTk5zIGZvciBGZWF0dXJlIExlYXJuaW5nDQoNCiAgICAjIyBJSUkuIEV2YWx1YXRpb24gTWV0cmljcw0KDQogICAgMS4gICoqQWNjdXJhY3kgTWV0cmljcyoqDQoNCiAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgIFJNU0UgPSDiiJoozqMoeV90cnVlIC0geV9wcmVkKV4yIC8gbikNCiAgICAgICAgTUFFID0gzqN8eV90cnVlIC0geV9wcmVkfCAvIG4NCiAgICAgICAgYGBgDQoNCiAgICAyLiAgKipSYW5raW5nIE1ldHJpY3MqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBQcmVjaXNpb25AayA9IHJlbGV2YW50X2l0ZW1zQGsgLyBrDQogICAgICAgIFJlY2FsbEBrID0gcmVsZXZhbnRfaXRlbXNAayAvIHRvdGFsX3JlbGV2YW50X2l0ZW1zDQogICAgICAgIE5EQ0dAayA9IERDR0BrIC8gSURDR0BrDQogICAgICAgIGBgYA0KDQogICAgIyMgSVYuIEltcGxlbWVudGF0aW9uIENvbnNpZGVyYXRpb25zDQoNCiAgICAxLiAgKipDb2xkIFN0YXJ0IFByb2JsZW0qKg0KICAgICAgICAtICAgU29sdXRpb25zOg0KICAgICAgICAgICAgLSAgIEh5YnJpZCBhcHByb2FjaGVzDQogICAgICAgICAgICAtICAgQ29udGVudC1iYXNlZCBpbml0aWFsaXphdGlvbg0KICAgICAgICAgICAgLSAgIERlZmF1bHQgcmVjb21tZW5kYXRpb25zDQogICAgMi4gICoqU2NhbGFiaWxpdHkqKg0KICAgICAgICAtICAgVGVjaG5pcXVlczoNCiAgICAgICAgICAgIC0gICBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24NCiAgICAgICAgICAgIC0gICBTYW1wbGluZw0KICAgICAgICAgICAgLSAgIERpc3RyaWJ1dGVkIGNvbXB1dGluZw0KICAgIDMuICAqKlJlYWwtdGltZSBVcGRhdGVzKioNCiAgICAgICAgLSAgIE9ubGluZSBsZWFybmluZw0KICAgICAgICAtICAgSW5jcmVtZW50YWwgdXBkYXRlcw0KICAgICAgICAtICAgU3RyZWFtIHByb2Nlc3NpbmcNCg0KICAgICMjIFYuIEZ1dHVyZSBUcmVuZHMNCg0KICAgIDEuICBEZWVwIExlYXJuaW5nIEludGVncmF0aW9uDQogICAgMi4gIFJlaW5mb3JjZW1lbnQgTGVhcm5pbmcNCiAgICAzLiAgR3JhcGggTmV1cmFsIE5ldHdvcmtzDQogICAgNC4gIE5hdHVyYWwgTGFuZ3VhZ2UgUHJvY2Vzc2luZw0KICAgIDUuICBGZWRlcmF0ZWQgTGVhcm5pbmcNCiAgICA2LiAgRXhwbGFpbmFibGUgQUkNCg0KICAgICMjIFZJLiBCZW5lZml0cyBhbmQgQXBwbGljYXRpb25zDQoNCiAgICAxLiAgUGVyc29uYWxpemVkIENvbnRlbnQgRGVsaXZlcnkNCiAgICAyLiAgSW5jcmVhc2VkIFVzZXIgRW5nYWdlbWVudA0KICAgIDMuICBSZXZlbnVlIEdyb3d0aA0KICAgIDQuICBJbXByb3ZlZCBVc2VyIEV4cGVyaWVuY2UNCiAgICA1LiAgQXV0b21hdGVkIERlY2lzaW9uIE1ha2luZw0KICAgIDYuICBTY2FsYWJsZSBTb2x1dGlvbnMNCg0KW3N2ZA0Kc3R1ZHldKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9yZWNvbW1lbmRlci1zeXN0ZW1zLWEtY29tcGxldGUtZ3VpZGUtdG8tbWFjaGluZS1sZWFybmluZy1tb2RlbHMtOTZkM2Y5NGVhNzQ4KQ0KDQpbYW5vdGhlciBnb29kDQpsaW5rXShodHRwczovL2luc2lnaHRzLmRhZmZvZGlsc3cuY29tL2Jsb2cvbWFjaGluZS1sZWFybmluZy1hbGdvcml0aG1zLWZvci1yZWNvbW1lbmRhdGlvbi1lbmdpbmVzKQ0K