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

  1. 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.
  2. Matrix Representation:
    • Data is represented as a matrix:
      • Rows: Users.
      • Columns: Movies.
      • Entries: Ratings (or 0 if no rating).
  3. 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).
  4. 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.

  1. Interpretation:
    • \(U\): User latent factors.
    • \(V\): Movie latent factors.
    • \(\Sigma\): Strength of latent factors.
  2. 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

  1. Load Data:
    • Extract user-movie ratings, movie details.
  2. Create Rating Matrix:
    • Convert user-movie interactions into a matrix with rows as users and columns as movies.
  3. Normalize Data:
    • Normalize ratings for each movie to handle variations.
  4. Compute SVD:
    • Use libraries (e.g., NumPy or SciPy) to decompose the matrix into \(U, \Sigma,\) and \(V^T\).
  5. Find Recommendations:
    • Use top singular values and corresponding vectors to:
      • Recommend movies for a user.
      • Find similar movies.
  6. Optimize with Reduced SVD:
    • Use only top \(k\) singular values (e.g., based on elbow point in singular value plot).

Visualization

  1. Matrix Decomposition:
    • Original matrix \(A\)\(U \Sigma V^T\).
    • \(\Sigma\): Singular values plotted to identify the elbow point (optimal \(k\)).
  2. 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

  1. Movie Recommendations:
    • Suggest movies for users (Netflix-style).
    • Find similar movies (content-based).
  2. Advertising:
    • Recommend ads to users with similar interests.
  3. 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

  1. 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.
  2. Dataset Description:
    • Input: User ratings of movies.
    • Structure:
      • Users (rows) × Movies (columns) matrix where entries represent ratings.
      • Ratings are normalized for consistency.
  3. 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.
  4. 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 \]
  5. 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.
  6. 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

  1. 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.
  2. Interpretation:
    • \(U\): Represents user embeddings.
    • \(V^T\): Represents item embeddings.
    • \(\Sigma\): Importance of features (higher values = more importance).
  3. Dimensionality Reduction:
    • Select top \(k\) singular values.
    • Reduced matrix: \[ A_k = U_k \Sigma_k V_k^T \]

Implementation Steps

  1. Data Preparation:

    • Construct a user-movie matrix with ratings.
    • Normalize the ratings.
  2. 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, :]
  3. 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

  1. Optimal \(k\):
    • Use the “elbow method” or cross-validation on a subset of data.
  2. Cold Start Handling:
    • Initialize new users/movies with averages or most popular items.
  3. Comparison to PCA:
    • Both PCA and SVD reduce dimensions by capturing maximum variance; SVD is more general and directly applicable to user-item matrices.
  4. 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.

svd study

another good link

LS0tDQp0aXRsZTogIlNWRCBsZWN0dXJlOiBNTDczMzE6IDIwX25vdl8yMDI0Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCjwhLS0gR29vZ2xlIHRhZyAoZ3RhZy5qcykgLS0+DQoNCmBgYHs9aHRtbH0NCjxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3d3dy5nb29nbGV0YWdtYW5hZ2VyLmNvbS9ndGFnL2pzP2lkPUctQ1YyNjQ4R1FNSyI+PC9zY3JpcHQ+DQpgYGANCg0KYGBgez1odG1sfQ0KPHNjcmlwdD4NCiAgd2luZG93LmRhdGFMYXllciA9IHdpbmRvdy5kYXRhTGF5ZXIgfHwgW107DQogIGZ1bmN0aW9uIGd0YWcoKXtkYXRhTGF5ZXIucHVzaChhcmd1bWVudHMpO30NCiAgZ3RhZygnanMnLCBuZXcgRGF0ZSgpKTsNCg0KICBndGFnKCdjb25maWcnLCAnRy1DVjI2NDhHUU1LJyk7DQo8L3NjcmlwdD4NCmBgYA0KDQpTdHJlYW0gTmFtZSBUZXhhc2NoaWtraXRhIFN0cmVhbSBVUkwgPGh0dHBzOi8vcnB1YnMuY29tL1RleGFzY2hpa2tpdGE+DQpTdHJlYW0gSUQgOTk2MjMyNDE3OSBNZWFzdXJlbWVudCBJZCBHLUNWMjY0OEdRTUsNCg0KIyMjICoqU3R1ZHkgR3VpZGU6IFJlY29tbWVuZGVyIFN5c3RlbXMgVXNpbmcgU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSoqDQoNCiMjIyMgKipLZXkgQ29uY2VwdHMqKg0KDQoxLiAgKipSZWNvbW1lbmRlciBTeXN0ZW1zKio6DQogICAgLSAgIFN5c3RlbXMgZGVzaWduZWQgdG8gc3VnZ2VzdCBpdGVtcyAoZS5nLiwgbW92aWVzKSBiYXNlZCBvbiB1c2VyDQogICAgICAgIHByZWZlcmVuY2VzIG9yIGhpc3RvcnkuDQogICAgLSAgIEV4YW1wbGU6IE5ldGZsaXggc3VnZ2VzdGluZyBtb3ZpZXMgYmFzZWQgb24gdXNlciByYXRpbmdzIGFuZA0KICAgICAgICB3YXRjaCBoaXN0b3J5Lg0KMi4gICoqTWF0cml4IFJlcHJlc2VudGF0aW9uKio6DQogICAgLSAgIERhdGEgaXMgcmVwcmVzZW50ZWQgYXMgYSBtYXRyaXg6DQogICAgICAgIC0gICBSb3dzOiBVc2Vycy4NCiAgICAgICAgLSAgIENvbHVtbnM6IE1vdmllcy4NCiAgICAgICAgLSAgIEVudHJpZXM6IFJhdGluZ3MgKG9yIDAgaWYgbm8gcmF0aW5nKS4NCjMuICAqKk1hdHJpeCBGYWN0b3JpemF0aW9uKio6DQogICAgLSAgIERlY29tcG9zaW5nIHRoZSBtYXRyaXggaW50byB0aHJlZSBzbWFsbGVyIG1hdHJpY2VzOg0KICAgICAgICAtICAgJEEgPSBVIFxTaWdtYSBWXlQkDQogICAgICAgIC0gICAkVSQ6IFVzZXIgbGF0ZW50IGZhY3RvcnMgKHVzZXIgZW1iZWRkaW5nKS4NCiAgICAgICAgLSAgICRcU2lnbWEkOiBEaWFnb25hbCBtYXRyaXggd2l0aCBzaW5ndWxhciB2YWx1ZXMuDQogICAgICAgIC0gICAkViQ6IE1vdmllIGxhdGVudCBmYWN0b3JzIChtb3ZpZSBlbWJlZGRpbmcpLg0KNC4gICoqU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSoqOg0KICAgIC0gICBBIG1hdGhlbWF0aWNhbCBtZXRob2QgdG8gZGVjb21wb3NlIGEgbWF0cml4Lg0KICAgIC0gICBDYXB0dXJlcyBsYXRlbnQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHVzZXJzIGFuZCBtb3ZpZXMuDQogICAgLSAgIFJlbW92ZXMgbm9pc2UgYnkgY29uc2lkZXJpbmcgdG9wIHNpbmd1bGFyIHZhbHVlcyAoZGltZW5zaW9uYWxpdHkNCiAgICAgICAgcmVkdWN0aW9uKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipNYXRoZW1hdGljYWwgUmVwcmVzZW50YXRpb24qKg0KDQpHaXZlbiBhIG1hdHJpeCAkQSQ6IDEuICoqRGVjb21wb3NpdGlvbioqOiAkQSA9IFUgXFNpZ21hIFZeVCQsIHdoZXJlOiAtDQokVSQgYW5kICRWJCBhcmUgb3J0aG9nb25hbCBtYXRyaWNlcy4gLSAkXFNpZ21hJCBpcyBhIGRpYWdvbmFsIG1hdHJpeA0Kd2l0aCBzaW5ndWxhciB2YWx1ZXMgaW4gZGVzY2VuZGluZyBvcmRlci4NCg0KMi4gICoqSW50ZXJwcmV0YXRpb24qKjoNCiAgICAtICAgJFUkOiBVc2VyIGxhdGVudCBmYWN0b3JzLg0KICAgIC0gICAkViQ6IE1vdmllIGxhdGVudCBmYWN0b3JzLg0KICAgIC0gICAkXFNpZ21hJDogU3RyZW5ndGggb2YgbGF0ZW50IGZhY3RvcnMuDQozLiAgKipDb3NpbmUgU2ltaWxhcml0eSoqOiBVc2VkIHRvIG1lYXN1cmUgc2ltaWxhcml0eSBiZXR3ZWVuIGl0ZW1zOiAkJA0KICAgIFx0ZXh0e0Nvc2luZSBTaW1pbGFyaXR5fSA9IFxmcmFje1xtYXRoYmZ7YX0gXGNkb3QgXG1hdGhiZntifX17XHxcbWF0aGJme2F9XHwgXHxcbWF0aGJme2J9XHx9DQogICAgJCQNCiAgICAtICAgQXBwbGllZCB0byBjb2x1bW5zIG9mICRWJCB0byBmaW5kIHNpbWlsYXIgbW92aWVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkltcGxlbWVudGF0aW9uIFN0ZXBzKioNCg0KMS4gICoqTG9hZCBEYXRhKio6DQogICAgLSAgIEV4dHJhY3QgdXNlci1tb3ZpZSByYXRpbmdzLCBtb3ZpZSBkZXRhaWxzLg0KMi4gICoqQ3JlYXRlIFJhdGluZyBNYXRyaXgqKjoNCiAgICAtICAgQ29udmVydCB1c2VyLW1vdmllIGludGVyYWN0aW9ucyBpbnRvIGEgbWF0cml4IHdpdGggcm93cyBhcyB1c2Vycw0KICAgICAgICBhbmQgY29sdW1ucyBhcyBtb3ZpZXMuDQozLiAgKipOb3JtYWxpemUgRGF0YSoqOg0KICAgIC0gICBOb3JtYWxpemUgcmF0aW5ncyBmb3IgZWFjaCBtb3ZpZSB0byBoYW5kbGUgdmFyaWF0aW9ucy4NCjQuICAqKkNvbXB1dGUgU1ZEKio6DQogICAgLSAgIFVzZSBsaWJyYXJpZXMgKGUuZy4sIE51bVB5IG9yIFNjaVB5KSB0byBkZWNvbXBvc2UgdGhlIG1hdHJpeA0KICAgICAgICBpbnRvICRVLCBcU2lnbWEsJCBhbmQgJFZeVCQuDQo1LiAgKipGaW5kIFJlY29tbWVuZGF0aW9ucyoqOg0KICAgIC0gICBVc2UgdG9wIHNpbmd1bGFyIHZhbHVlcyBhbmQgY29ycmVzcG9uZGluZyB2ZWN0b3JzIHRvOg0KICAgICAgICAtICAgUmVjb21tZW5kIG1vdmllcyBmb3IgYSB1c2VyLg0KICAgICAgICAtICAgRmluZCBzaW1pbGFyIG1vdmllcy4NCjYuICAqKk9wdGltaXplIHdpdGggUmVkdWNlZCBTVkQqKjoNCiAgICAtICAgVXNlIG9ubHkgdG9wICRrJCBzaW5ndWxhciB2YWx1ZXMgKGUuZy4sIGJhc2VkIG9uIGVsYm93IHBvaW50IGluDQogICAgICAgIHNpbmd1bGFyIHZhbHVlIHBsb3QpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKlZpc3VhbGl6YXRpb24qKg0KDQoxLiAgKipNYXRyaXggRGVjb21wb3NpdGlvbioqOg0KICAgIC0gICBPcmlnaW5hbCBtYXRyaXggJEEkIOKGkiAkVSBcU2lnbWEgVl5UJC4NCiAgICAtICAgJFxTaWdtYSQ6IFNpbmd1bGFyIHZhbHVlcyBwbG90dGVkIHRvIGlkZW50aWZ5IHRoZSBlbGJvdyBwb2ludA0KICAgICAgICAob3B0aW1hbCAkayQpLg0KMi4gICoqUmVjb21tZW5kYXRpb24gRXhhbXBsZSoqOg0KICAgIC0gICBWaXN1YWxpemUgdG9wIHJlY29tbWVuZGF0aW9ucyBmb3IgYSBtb3ZpZSBvciB1c2VyLg0KDQojIyMjICoqQ29kZSBFeGFtcGxlKioNCg0KYGBgIHB5dGhvbg0KaW1wb3J0IG51bXB5IGFzIG5wDQpmcm9tIG51bXB5LmxpbmFsZyBpbXBvcnQgc3ZkDQpmcm9tIHNrbGVhcm4ubWV0cmljcy5wYWlyd2lzZSBpbXBvcnQgY29zaW5lX3NpbWlsYXJpdHkNCg0KIyBTdGVwIDE6IENvbnN0cnVjdCByYXRpbmcgbWF0cml4DQpyYXRpbmdzX21hdHJpeCA9IG5wLnplcm9zKChudW1fdXNlcnMsIG51bV9tb3ZpZXMpKSAgIyBFeGFtcGxlOiBGaWxsIHdpdGggYWN0dWFsIHJhdGluZ3MgZGF0YQ0KDQojIFN0ZXAgMjogTm9ybWFsaXplIHJhdGluZ3MNCm1lYW5fcmF0aW5ncyA9IG5wLm1lYW4ocmF0aW5nc19tYXRyaXgsIGF4aXM9MCkNCm5vcm1hbGl6ZWRfbWF0cml4ID0gcmF0aW5nc19tYXRyaXggLSBtZWFuX3JhdGluZ3MNCg0KIyBTdGVwIDM6IENvbXB1dGUgU1ZEDQpVLCBTLCBWVCA9IHN2ZChub3JtYWxpemVkX21hdHJpeCwgZnVsbF9tYXRyaWNlcz1GYWxzZSkNClNfZGlhZyA9IG5wLmRpYWcoUykNCg0KIyBTdGVwIDQ6IENob29zZSB0b3AgayBjb21wb25lbnRzIChyZWR1Y2Ugbm9pc2UpDQprID0gMzAgICMgT3B0aW1hbCBudW1iZXIgb2Ygc2luZ3VsYXIgdmFsdWVzDQpVX2sgPSBVWzosIDprXQ0KU19rID0gU19kaWFnWzprLCA6a10NClZUX2sgPSBWVFs6aywgOl0NCg0KIyBTdGVwIDU6IFJlY29tbWVuZGF0aW9ucw0KZGVmIHJlY29tbWVuZF9tb3ZpZXMobW92aWVfaWQsIHRvcF9uPTEwKToNCiAgICBtb3ZpZV92ZWN0b3IgPSBWVF9rWzosIG1vdmllX2lkXQ0KICAgIHNpbWlsYXJpdGllcyA9IGNvc2luZV9zaW1pbGFyaXR5KG1vdmllX3ZlY3Rvci5yZXNoYXBlKDEsIC0xKSwgVlRfay5UKQ0KICAgIHRvcF9pbmRpY2VzID0gbnAuYXJnc29ydChzaW1pbGFyaXRpZXNbMF0pWy10b3BfbjpdDQogICAgcmV0dXJuIHRvcF9pbmRpY2VzWzo6LTFdDQoNCiMgRXhhbXBsZTogUmVjb21tZW5kIG1vdmllcyBzaW1pbGFyIHRvIFRveSBTdG9yeSAobW92aWVfaWQ9MSkNCnNpbWlsYXJfbW92aWVzID0gcmVjb21tZW5kX21vdmllcyhtb3ZpZV9pZD0xKQ0KcHJpbnQoIlJlY29tbWVuZGVkIE1vdmllczoiLCBzaW1pbGFyX21vdmllcykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkFwcGxpY2F0aW9ucyoqDQoNCjEuICAqKk1vdmllIFJlY29tbWVuZGF0aW9ucyoqOg0KICAgIC0gICBTdWdnZXN0IG1vdmllcyBmb3IgdXNlcnMgKE5ldGZsaXgtc3R5bGUpLg0KICAgIC0gICBGaW5kIHNpbWlsYXIgbW92aWVzIChjb250ZW50LWJhc2VkKS4NCjIuICAqKkFkdmVydGlzaW5nKio6DQogICAgLSAgIFJlY29tbWVuZCBhZHMgdG8gdXNlcnMgd2l0aCBzaW1pbGFyIGludGVyZXN0cy4NCjMuICAqKk5vaXNlIFJlZHVjdGlvbioqOg0KICAgIC0gICBIYW5kbGUgbm9pc3kgZGF0YSBieSBmb2N1c2luZyBvbiB0b3Agc2luZ3VsYXIgdmFsdWVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkluc2lnaHRzKioNCg0KLSAgICoqQWR2YW50YWdlcyoqOg0KICAgIC0gICBTaW1wbGUsIGludGVycHJldGFibGUsIGFuZCBlZmZlY3RpdmUgZm9yIGNvbGxhYm9yYXRpdmUNCiAgICAgICAgZmlsdGVyaW5nLg0KICAgIC0gICBDYW4gaGFuZGxlIHNwYXJzZSBkYXRhLg0KLSAgICoqTGltaXRhdGlvbnMqKjoNCiAgICAtICAgQ29sZCBzdGFydCBwcm9ibGVtOiBDYW5ub3QgaGFuZGxlIG5ldyB1c2Vycy9tb3ZpZXMgd2l0aG91dCBwcmlvcg0KICAgICAgICBkYXRhLg0KICAgIC0gICBObyBnZW5yZSBvciBtZXRhZGF0YSB1c2FnZSB1bmxlc3MgZXh0ZW5kZWQuDQoNCkJ5IHVuZGVyc3RhbmRpbmcgU1ZELCB5b3UgY2FuIGltcGxlbWVudCBlZmZpY2llbnQgcmVjb21tZW5kZXIgc3lzdGVtcw0KYW5kIGV4cGxvcmUgYWR2YW5jZWQgdGVjaG5pcXVlcyBsaWtlIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgb3INCm5ldXJhbCBuZXR3b3JrLWJhc2VkIG1vZGVscyBmb3IgaW1wcm92ZWQgcmVzdWx0cy4NCg0KIyMjIFN1bW1hcnkgb2YgTGVjdHVyZSBvbiBTVkQgZm9yIFJlY29tbWVuZGVyIFN5c3RlbXMNCg0KIyMjIyAqKktleSBQb2ludHMqKg0KDQoxLiAgKipJbnRyb2R1Y3Rpb24gdG8gU1ZEKio6DQogICAgLSAgIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaXMgdXNlZCBpbiByZWNvbW1lbmRhdGlvbg0KICAgICAgICBzeXN0ZW1zIHRvIGRlY29tcG9zZSB1c2VyLWl0ZW0gbWF0cmljZXMgaW50byB0aHJlZSBtYXRyaWNlcyAkVSQsDQogICAgICAgICRcU2lnbWEkLCBhbmQgJFZeVCQuDQogICAgLSAgIFRoaXMgcHJvY2VzcyBwcm92aWRlcyB2ZWN0b3IgcmVwcmVzZW50YXRpb25zIChlbWJlZGRpbmdzKSBvZg0KICAgICAgICB1c2VycyBhbmQgaXRlbXMgdGhhdCBjYXB0dXJlIGxhdGVudCByZWxhdGlvbnNoaXBzLg0KMi4gICoqRGF0YXNldCBEZXNjcmlwdGlvbioqOg0KICAgIC0gICBJbnB1dDogVXNlciByYXRpbmdzIG9mIG1vdmllcy4NCiAgICAtICAgU3RydWN0dXJlOg0KICAgICAgICAtICAgVXNlcnMgKHJvd3MpIMOXIE1vdmllcyAoY29sdW1ucykgbWF0cml4IHdoZXJlIGVudHJpZXMNCiAgICAgICAgICAgIHJlcHJlc2VudCByYXRpbmdzLg0KICAgICAgICAtICAgUmF0aW5ncyBhcmUgbm9ybWFsaXplZCBmb3IgY29uc2lzdGVuY3kuDQozLiAgKipNYXRyaXggRmFjdG9yaXphdGlvbioqOg0KICAgIC0gICBNYXRyaXggJEEkICh1c2VyLW1vdmllIHJhdGluZ3MpIGlzIGZhY3Rvcml6ZWQgYXM6ICQkDQogICAgICAgIEEgPSBVIFxTaWdtYSBWXlQNCiAgICAgICAgJCQNCiAgICAgICAgLSAgICRVJDogRW1iZWRkaW5nIGZvciB1c2Vycy4NCiAgICAgICAgLSAgICRWJDogRW1iZWRkaW5nIGZvciBtb3ZpZXMuDQogICAgICAgIC0gICAkXFNpZ21hJDogRGlhZ29uYWwgbWF0cml4IGNvbnRhaW5pbmcgc2luZ3VsYXIgdmFsdWVzIHRoYXQNCiAgICAgICAgICAgIHJlcHJlc2VudCB0aGUgc3RyZW5ndGggb2YgY29ycmVzcG9uZGluZyBsYXRlbnQgZmVhdHVyZXMuDQo0LiAgKipEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24qKjoNCiAgICAtICAgTm9pc2UgcmVkdWN0aW9uIGlzIGFjaGlldmVkIGJ5IHNlbGVjdGluZyB0b3AgJGskIHNpbmd1bGFyIHZhbHVlcw0KICAgICAgICAoJFxTaWdtYV9rJCkgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgdmVjdG9ycy4NCiAgICAtICAgVGhpcyBhcHByb3hpbWF0ZXMgJEEkIGFzOiAkJA0KICAgICAgICBBX2sgXGFwcHJveCBVX2sgXFNpZ21hX2sgVl9rXlQNCiAgICAgICAgJCQNCjUuICAqKkFwcGxpY2F0aW9ucyoqOg0KICAgIC0gICAqKlJlY29tbWVuZGF0aW9uKio6IEZpbmQgbW92aWVzIHNpbWlsYXIgdG8gYSBnaXZlbiBtb3ZpZS4NCiAgICAtICAgKipEZW5vaXNpbmcqKjogUmVkdWNlIHRoZSBlZmZlY3Qgb2Ygbm9pc3ksIGxlc3MgcmVsZXZhbnQNCiAgICAgICAgZmVhdHVyZXMuDQogICAgLSAgICoqQ29sZCBTdGFydCBQcm9ibGVtKio6IEFkZHJlc3MgbmV3IHVzZXIvbW92aWUgc2NlbmFyaW9zIHdpdGgNCiAgICAgICAgZGVmYXVsdCByZWNvbW1lbmRhdGlvbnMuDQo2LiAgKipJbXBsZW1lbnRhdGlvbioqOg0KICAgIC0gICBVc2UgUHl0aG9uJ3MgYG51bXB5LmxpbmFsZy5zdmRgIGZvciBTVkQgY29tcHV0YXRpb24uDQogICAgLSAgIFNlbGVjdCB0aGUgb3B0aW1hbCAkayQgYnkgZXhhbWluaW5nIHRoZSBzaW5ndWxhciB2YWx1ZXMnDQogICAgICAgIGRyb3Atb2ZmIChlbGJvdyBtZXRob2QpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFN0dWR5IEd1aWRlDQoNCiMjIyMgKipNYXRoZW1hdGljYWwgQmFja2dyb3VuZCoqDQoNCjEuICAqKlNWRCBSZXByZXNlbnRhdGlvbioqOg0KICAgIC0gICBEZWNvbXBvc2UgYSBtYXRyaXggJEEkOiAkJA0KICAgICAgICBBID0gVSBcU2lnbWEgVl5UDQogICAgICAgICQkDQogICAgLSAgIFByb3BlcnRpZXM6DQogICAgICAgIC0gICAkVSQgYW5kICRWXlQkIGFyZSBvcnRob2dvbmFsIG1hdHJpY2VzLg0KICAgICAgICAtICAgJFxTaWdtYSQgY29udGFpbnMgc2luZ3VsYXIgdmFsdWVzIHNvcnRlZCBpbiBkZXNjZW5kaW5nDQogICAgICAgICAgICBvcmRlci4NCjIuICAqKkludGVycHJldGF0aW9uKio6DQogICAgLSAgICRVJDogUmVwcmVzZW50cyB1c2VyIGVtYmVkZGluZ3MuDQogICAgLSAgICRWXlQkOiBSZXByZXNlbnRzIGl0ZW0gZW1iZWRkaW5ncy4NCiAgICAtICAgJFxTaWdtYSQ6IEltcG9ydGFuY2Ugb2YgZmVhdHVyZXMgKGhpZ2hlciB2YWx1ZXMgPSBtb3JlDQogICAgICAgIGltcG9ydGFuY2UpLg0KMy4gICoqRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uKio6DQogICAgLSAgIFNlbGVjdCB0b3AgJGskIHNpbmd1bGFyIHZhbHVlcy4NCiAgICAtICAgUmVkdWNlZCBtYXRyaXg6ICQkDQogICAgICAgIEFfayA9IFVfayBcU2lnbWFfayBWX2teVA0KICAgICAgICAkJA0KDQojIyMjICoqSW1wbGVtZW50YXRpb24gU3RlcHMqKg0KDQoxLiAgKipEYXRhIFByZXBhcmF0aW9uKio6DQoNCiAgICAtICAgQ29uc3RydWN0IGEgdXNlci1tb3ZpZSBtYXRyaXggd2l0aCByYXRpbmdzLg0KICAgIC0gICBOb3JtYWxpemUgdGhlIHJhdGluZ3MuDQoNCjIuICAqKkNvbXB1dGUgU1ZEKio6DQoNCiAgICBgYGAgcHl0aG9uDQogICAgaW1wb3J0IG51bXB5IGFzIG5wDQoNCiAgICAjIENvbXB1dGUgU1ZEDQogICAgVSwgUywgVnQgPSBucC5saW5hbGcuc3ZkKEEsIGZ1bGxfbWF0cmljZXM9RmFsc2UpDQoNCiAgICAjIFNlbGVjdCB0b3AgayBjb21wb25lbnRzDQogICAgayA9IDMwDQogICAgVV9rID0gVVs6LCA6a10NCiAgICBTX2sgPSBucC5kaWFnKFNbOmtdKQ0KICAgIFZ0X2sgPSBWdFs6aywgOl0NCiAgICBgYGANCg0KMy4gICoqQ29zaW5lIFNpbWlsYXJpdHkqKjoNCg0KICAgIC0gICBGaW5kIHNpbWlsYXIgbW92aWVzIHVzaW5nIGNvc2luZSBzaW1pbGFyaXR5IGJldHdlZW4gbW92aWUNCiAgICAgICAgdmVjdG9ycyBpbiAkVl9rJC4NCg0KICAgIGBgYCBweXRob24NCiAgICBmcm9tIHNrbGVhcm4ubWV0cmljcy5wYWlyd2lzZSBpbXBvcnQgY29zaW5lX3NpbWlsYXJpdHkNCg0KICAgICMgQ29tcHV0ZSBjb3NpbmUgc2ltaWxhcml0eQ0KICAgIHNpbWlsYXJpdGllcyA9IGNvc2luZV9zaW1pbGFyaXR5KFZ0X2suVCkNCg0KICAgICMgUmVjb21tZW5kIHRvcCBuIG1vdmllcw0KICAgIG1vdmllX2lkID0gMCAgIyBNb3ZpZSBvZiBpbnRlcmVzdA0KICAgIHRvcF9uID0gMTANCiAgICBzaW1pbGFyX21vdmllcyA9IG5wLmFyZ3NvcnQoc2ltaWxhcml0aWVzW21vdmllX2lkXSlbLXRvcF9uOl0NCiAgICBgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyBWaXN1YWxpemF0aW9uDQoNCiMjIyMgKipTaW5ndWxhciBWYWx1ZXMgKFNjcmVlIFBsb3QpKioNCg0KLSAgIFBsb3QgdGhlIHNpbmd1bGFyIHZhbHVlcyB0byBpZGVudGlmeSB0aGUgZWxib3cgcG9pbnQgd2hlcmUgbW9zdA0KICAgIGluZm9ybWF0aW9uIGlzIGNhcHR1cmVkOg0KDQogICAgYGBgIHB5dGhvbg0KICAgIGltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KICAgIHBsdC5wbG90KFMpDQogICAgcGx0LnhsYWJlbCgnQ29tcG9uZW50IEluZGV4JykNCiAgICBwbHQueWxhYmVsKCdTaW5ndWxhciBWYWx1ZScpDQogICAgcGx0LnRpdGxlKCdTY3JlZSBQbG90JykNCiAgICBwbHQuc2hvdygpDQogICAgYGBgDQoNCiMjIyMgKipSZWNvbW1lbmRhdGlvbiBFeGFtcGxlKioNCg0KLSAgIEhlYXRtYXAgb2YgdXNlci1pdGVtIGludGVyYWN0aW9uIGJlZm9yZSBhbmQgYWZ0ZXIgZGltZW5zaW9uYWxpdHkNCiAgICByZWR1Y3Rpb246DQoNCiAgICBgYGAgcHl0aG9uDQogICAgaW1wb3J0IHNlYWJvcm4gYXMgc25zDQoNCiAgICBzbnMuaGVhdG1hcChBLCBjbWFwPSdjb29sd2FybScsIGNiYXI9VHJ1ZSkNCiAgICBzbnMuaGVhdG1hcChVX2sgQCBTX2sgQCBWdF9rLCBjbWFwPSdjb29sd2FybScsIGNiYXI9VHJ1ZSkNCiAgICBgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyBBZGRpdGlvbmFsIFRpcHMNCg0KMS4gICoqT3B0aW1hbCoqICRrJDoNCiAgICAtICAgVXNlIHRoZSAiZWxib3cgbWV0aG9kIiBvciBjcm9zcy12YWxpZGF0aW9uIG9uIGEgc3Vic2V0IG9mIGRhdGEuDQoyLiAgKipDb2xkIFN0YXJ0IEhhbmRsaW5nKio6DQogICAgLSAgIEluaXRpYWxpemUgbmV3IHVzZXJzL21vdmllcyB3aXRoIGF2ZXJhZ2VzIG9yIG1vc3QgcG9wdWxhciBpdGVtcy4NCjMuICAqKkNvbXBhcmlzb24gdG8gUENBKio6DQogICAgLSAgIEJvdGggUENBIGFuZCBTVkQgcmVkdWNlIGRpbWVuc2lvbnMgYnkgY2FwdHVyaW5nIG1heGltdW0NCiAgICAgICAgdmFyaWFuY2U7IFNWRCBpcyBtb3JlIGdlbmVyYWwgYW5kIGRpcmVjdGx5IGFwcGxpY2FibGUgdG8NCiAgICAgICAgdXNlci1pdGVtIG1hdHJpY2VzLg0KNC4gICoqRXh0ZW5zaW9ucyoqOg0KICAgIC0gICBJbmNvcnBvcmF0ZSBnZW5yZXMgb3IgdXNlciBkZW1vZ3JhcGhpY3MgZm9yIGh5YnJpZA0KICAgICAgICByZWNvbW1lbmRhdGlvbiBzeXN0ZW1zLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFNhbXBsZSBDb2RlIChQeXRob24pDQoNCmBgYCBweXRob24NCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBza2xlYXJuLm1ldHJpY3MucGFpcndpc2UgaW1wb3J0IGNvc2luZV9zaW1pbGFyaXR5DQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgU2FtcGxlIHVzZXItaXRlbSBtYXRyaXgNCkEgPSBucC5hcnJheShbWzUsIDQsIDAsIDFdLA0KICAgICAgICAgICAgICBbNCwgMCwgMCwgMV0sDQogICAgICAgICAgICAgIFsxLCAxLCAwLCA1XSwNCiAgICAgICAgICAgICAgWzEsIDAsIDQsIDRdLA0KICAgICAgICAgICAgICBbMCwgMSwgNSwgNF1dKQ0KDQojIFBlcmZvcm0gU1ZEDQpVLCBTLCBWdCA9IG5wLmxpbmFsZy5zdmQoQSwgZnVsbF9tYXRyaWNlcz1GYWxzZSkNCg0KIyBTZWxlY3QgdG9wLWsgY29tcG9uZW50cw0KayA9IDINClVfayA9IFVbOiwgOmtdDQpTX2sgPSBucC5kaWFnKFNbOmtdKQ0KVnRfayA9IFZ0WzprLCA6XQ0KDQojIFJlY29uc3RydWN0IHJlZHVjZWQgbWF0cml4DQpBX2sgPSBVX2sgQCBTX2sgQCBWdF9rDQoNCiMgVmlzdWFsaXplIG9yaWdpbmFsIGFuZCByZWR1Y2VkIG1hdHJpY2VzDQpwbHQuc3VicGxvdCgxLCAyLCAxKQ0KcGx0LnRpdGxlKCJPcmlnaW5hbCBNYXRyaXgiKQ0Kc25zLmhlYXRtYXAoQSwgYW5ub3Q9VHJ1ZSwgY21hcD0nY29vbHdhcm0nKQ0KDQpwbHQuc3VicGxvdCgxLCAyLCAyKQ0KcGx0LnRpdGxlKCJSZWR1Y2VkIE1hdHJpeCIpDQpzbnMuaGVhdG1hcChBX2ssIGFubm90PVRydWUsIGNtYXA9J2Nvb2x3YXJtJykNCnBsdC5zaG93KCkNCmBgYA0KDQpUaGlzIGNvZGUgaW1wbGVtZW50cyB0aGUgY29uY2VwdHMgZGlzY3Vzc2VkIGFuZCBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YNCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvbiByZWNvbW1lbmRhdGlvbnMuDQoNCi0gICBNYWNoaW5lIGxlYXJuaW5nLWJhc2VkIHJlY29tbWVuZGF0aW9uIHN5c3RlbXMgYXJlIHBvd2VyZnVsIGVuZ2luZXMNCiAgICB1c2luZyBtYWNoaW5lIGxlYXJuaW5nIChNTCkgYWxnb3JpdGhtcyB0byBzZWdtZW50IGN1c3RvbWVycyBiYXNlZCBvbg0KICAgIHVzZXIgZGF0YSBhbmQgYmVoYXZpb3JhbCBwYXR0ZXJucyAoc3VjaCBhcyBwdXJjaGFzZSBhbmQgYnJvd3NpbmcNCiAgICBoaXN0b3J5LCBsaWtlcywgb3IgcmV2aWV3cykgYW5kIHRhcmdldCB0aGVtIHdpdGggcGVyc29uYWxpemVkDQogICAgcHJvZHVjdCBvciBjb250ZW50IHN1Z2dlc3Rpb25zLg0KDQotICAgUmVjb21tZW5kZXIgc3lzdGVtcyBhcmUgYSB0eXBlIG9mIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtDQogICAgZGVzaWduZWQgdG8gcHJvdmlkZSBwZXJzb25hbGl6ZWQgc3VnZ2VzdGlvbnMgYnkgYW5hbHl6aW5nIHVzZXIgZGF0YQ0KICAgIHRvIHByZWRpY3Qgd2hpY2ggaXRlbXMgd2lsbCBiZSBtb3N0IHJlbGV2YW50IGZvciBlYWNoIGluZGl2aWR1YWwNCiAgICAoc25pcHBldCAyKS4gVGhleSBoZWxwIG5hcnJvdyBkb3duIG9wdGlvbnMgYW5kIGltcHJvdmUgdGhlIHVzZXINCiAgICBleHBlcmllbmNlIGJ5IHRhaWxvcmluZyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gcHJlZmVyZW5jZXMgYW5kDQogICAgYmVoYXZpb3IgKHNuaXBwZXQgMykuIFRoZXJlIGFyZSB2YXJpb3VzIG1vZGVscyBhbmQgYXBwcm9hY2hlcyB1c2VkDQogICAgaW4gdGhlc2Ugc3lzdGVtcywgd2hpY2ggY2FuIGJlIGZ1cnRoZXIgZXhwbG9yZWQgaW4gcmVsZXZhbnQgY291cnNlcw0KICAgIG9yIGxpdGVyYXR1cmUgKHNuaXBwZXQgMSkuIEl0J3MgYWx3YXlzIGEgZ29vZCBpZGVhIHRvIHZlcmlmeQ0KICAgIGltcG9ydGFudCBkZXRhaWxzIGZyb20gcmVsaWFibGUgc291cmNlcy4NCg0KLSAgICoqQ29sbGFib3JhdGl2ZSBGaWx0ZXJpbmcqKg0KDQogICAgLSAgIENvbGxhYm9yYXRpdmUgZmlsdGVyaW5nIG1ha2VzIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB0aGUNCiAgICAgICAgcHJlZmVyZW5jZXMgYW5kIGJlaGF2aW9ycyBvZiBzaW1pbGFyIHVzZXJzLg0KDQogICAgLSAgIEl0IGFuYWx5emVzIHBhdHRlcm5zIGluIHVzZXIgcmF0aW5ncywgcHVyY2hhc2VzLCBvciBpbnRlcmFjdGlvbnMNCiAgICAgICAgdG8gaWRlbnRpZnkgdXNlcnMgd2l0aCBzaW1pbGFyIHRhc3Rlcy4NCg0KICAgIC0gICBUaGUgc3lzdGVtIHRoZW4gcmVjb21tZW5kcyBpdGVtcyB0aGF0IHNpbWlsYXIgdXNlcnMgaGF2ZSBsaWtlZA0KICAgICAgICBvciBpbnRlcmFjdGVkIHdpdGguDQoNCiAgICAtICAgS2V5IGFkdmFudGFnZXMgYXJlIHRoYXQgaXQgY2FuIG1ha2Ugc2VyZW5kaXBpdG91cw0KICAgICAgICByZWNvbW1lbmRhdGlvbnMgYW5kIGRvZXNuJ3QgcmVxdWlyZSBkZXRhaWxlZCBpdGVtIG1ldGFkYXRhLg0KDQogICAgLSAgIENoYWxsZW5nZXMgaW5jbHVkZSB0aGUgY29sZC1zdGFydCBwcm9ibGVtIChmb3IgbmV3IHVzZXJzL2l0ZW1zKQ0KICAgICAgICBhbmQgc3BhcnNpdHkgb2YgdXNlci1pdGVtIGludGVyYWN0aW9uIGRhdGEuDQoNCiAgICAqKkNvbnRlbnQtQmFzZWQgRmlsdGVyaW5nKioNCg0KICAgIC0gICBDb250ZW50LWJhc2VkIGZpbHRlcmluZyBtYWtlcyByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gdGhlDQogICAgICAgIGF0dHJpYnV0ZXMgb3IgZmVhdHVyZXMgb2YgdGhlIGl0ZW1zIHRoZW1zZWx2ZXMuDQoNCiAgICAtICAgSXQgYW5hbHl6ZXMgdGhlIGNvbnRlbnQsIG1ldGFkYXRhLCBvciBkZXNjcmlwdGlvbnMgb2YgaXRlbXMgYQ0KICAgICAgICB1c2VyIGhhcyBsaWtlZCBpbiB0aGUgcGFzdC4NCg0KICAgIC0gICBUaGUgc3lzdGVtIHRoZW4gcmVjb21tZW5kcyBvdGhlciBpdGVtcyB3aXRoIHNpbWlsYXIgY29udGVudA0KICAgICAgICBjaGFyYWN0ZXJpc3RpY3MuDQoNCiAgICAtICAgS2V5IGFkdmFudGFnZXMgYXJlIHRoYXQgaXQgY2FuIGhhbmRsZSB0aGUgY29sZC1zdGFydCBwcm9ibGVtIGFuZA0KICAgICAgICBkb2Vzbid0IHJlbHkgc29sZWx5IG9uIHVzZXIgaW50ZXJhY3Rpb25zLg0KDQogICAgLSAgIENoYWxsZW5nZXMgaW5jbHVkZSB0aGUgbmVlZCBmb3IgcmljaCBpdGVtIG1ldGFkYXRhIGFuZCB0aGUNCiAgICAgICAgcG90ZW50aWFsIGZvciBvdmVyc3BlY2lhbGl6YXRpb24gKHJlY29tbWVuZGluZyB2ZXJ5IHNpbWlsYXINCiAgICAgICAgaXRlbXMpLg0KDQogICAgTWFueSBtb2Rlcm4gcmVjb21tZW5kZXIgc3lzdGVtcyB1c2UgYSBoeWJyaWQgYXBwcm9hY2gsIGNvbWJpbmluZw0KICAgIGNvbGxhYm9yYXRpdmUgYW5kIGNvbnRlbnQtYmFzZWQgZmlsdGVyaW5nIHRvIGxldmVyYWdlIHRoZSBzdHJlbmd0aHMNCiAgICBvZiBlYWNoIG1ldGhvZC4NCg0KLSAgIA0KDQogICAgMS4gICoqQ29sbGFib3JhdGl2ZSBGaWx0ZXJpbmcgUmVjb21tZW5kZXJzKio6DQogICAgICAgIC0gICBCYXNlZCBvbiB1c2VyLXVzZXIgb3IgaXRlbS1pdGVtIHNpbWlsYXJpdGllcw0KICAgICAgICAtICAgTWFrZSByZWNvbW1lbmRhdGlvbnMgYmFzZWQgb24gdGhlIHByZWZlcmVuY2VzIGFuZCBiZWhhdmlvcnMNCiAgICAgICAgICAgIG9mIHNpbWlsYXIgdXNlcnMNCiAgICAgICAgLSAgIEV4YW1wbGVzOiBBbWF6b24ncyAiQ3VzdG9tZXJzIHdobyBib3VnaHQgdGhpcyBpdGVtIGFsc28NCiAgICAgICAgICAgIGJvdWdodC4uLiIsIE5ldGZsaXggbW92aWUgcmVjb21tZW5kYXRpb25zDQogICAgMi4gICoqQ29udGVudC1CYXNlZCBSZWNvbW1lbmRlcnMqKjoNCiAgICAgICAgLSAgIFJlY29tbWVuZCBpdGVtcyBzaW1pbGFyIHRvIHRoZSBvbmVzIGEgdXNlciBoYXMgbGlrZWQgaW4gdGhlDQogICAgICAgICAgICBwYXN0DQogICAgICAgIC0gICBBbmFseXplIHRoZSBjb250ZW50LCBtZXRhZGF0YSwgb3IgZGVzY3JpcHRpb25zIG9mIGl0ZW1zDQogICAgICAgIC0gICBFeGFtcGxlczogUmVjb21tZW5kaW5nIGJvb2tzIG9yIGFydGljbGVzIGJhc2VkIG9uIHRoZSB0b3BpY3MNCiAgICAgICAgICAgIG9yIGdlbnJlcyBhIHVzZXIgaGFzIHByZXZpb3VzbHkgZW5nYWdlZCB3aXRoDQogICAgMy4gICoqSHlicmlkIFJlY29tbWVuZGVycyoqOg0KICAgICAgICAtICAgQ29tYmluZSBjb2xsYWJvcmF0aXZlIGFuZCBjb250ZW50LWJhc2VkIGFwcHJvYWNoZXMNCiAgICAgICAgLSAgIENhbiBsZXZlcmFnZSB0aGUgc3RyZW5ndGhzIG9mIGVhY2ggdG8gb3ZlcmNvbWUgaW5kaXZpZHVhbA0KICAgICAgICAgICAgd2Vha25lc3Nlcw0KICAgICAgICAtICAgRXhhbXBsZXM6IENvbWJpbmluZyB1c2VyIHByZWZlcmVuY2VzIHdpdGggaXRlbSBmZWF0dXJlcyB0bw0KICAgICAgICAgICAgbWFrZSByZWNvbW1lbmRhdGlvbnMNCiAgICA0LiAgKipLbm93bGVkZ2UtQmFzZWQgUmVjb21tZW5kZXJzKio6DQogICAgICAgIC0gICBNYWtlIHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiBleHBsaWNpdCBrbm93bGVkZ2UgYWJvdXQgdXNlcg0KICAgICAgICAgICAgcHJlZmVyZW5jZXMgYW5kIGl0ZW0gZmVhdHVyZXMNCiAgICAgICAgLSAgIFVzZSBydWxlLWJhc2VkIG9yIGNhc2UtYmFzZWQgcmVhc29uaW5nIHRvIG1hdGNoIHVzZXIgbmVlZHMNCiAgICAgICAgICAgIHdpdGggaXRlbSBhdHRyaWJ1dGVzDQogICAgICAgIC0gICBFeGFtcGxlczogUmVjb21tZW5kaW5nIHByb2R1Y3RzIGJhc2VkIG9uIHVzZXItc3BlY2lmaWVkDQogICAgICAgICAgICByZXF1aXJlbWVudHMNCiAgICA1LiAgKipEZW1vZ3JhcGhpYyBSZWNvbW1lbmRlcnMqKjoNCiAgICAgICAgLSAgIE1ha2UgcmVjb21tZW5kYXRpb25zIGJhc2VkIG9uIHVzZXIgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24NCiAgICAgICAgLSAgIEFzc3VtZSB1c2VycyB3aXRoIHNpbWlsYXIgZGVtb2dyYXBoaWMgcHJvZmlsZXMgaGF2ZSBzaW1pbGFyDQogICAgICAgICAgICBwcmVmZXJlbmNlcw0KICAgICAgICAtICAgRXhhbXBsZXM6IFJlY29tbWVuZGluZyBwcm9kdWN0cyBvciBjb250ZW50IGJhc2VkIG9uIGFnZSwNCiAgICAgICAgICAgIGdlbmRlciwgbG9jYXRpb24sIGV0Yy4NCiAgICA2LiAgKipDb250ZXh0LUF3YXJlIFJlY29tbWVuZGVycyoqOg0KICAgICAgICAtICAgVGFrZSBpbnRvIGFjY291bnQgdGhlIGN1cnJlbnQgY29udGV4dCAodGltZSwgbG9jYXRpb24sDQogICAgICAgICAgICBkZXZpY2UsIGV0Yy4pIHdoZW4gbWFraW5nIHJlY29tbWVuZGF0aW9ucw0KICAgICAgICAtICAgQWRqdXN0IHJlY29tbWVuZGF0aW9ucyBiYXNlZCBvbiB0aGUgdXNlcidzIHNpdHVhdGlvbiBhbmQNCiAgICAgICAgICAgIGVudmlyb25tZW50DQogICAgICAgIC0gICBFeGFtcGxlczogU3VnZ2VzdGluZyBuZWFyYnkgcmVzdGF1cmFudHMgb3IgZXZlbnRzIGJhc2VkIG9uDQogICAgICAgICAgICB0aGUgdXNlcidzIGN1cnJlbnQgbG9jYXRpb24NCiAgICAgICAgLSAgIA0KICAgICAgICAgICAgMS4gICoqRS1jb21tZXJjZSBhbmQgUmV0YWlsKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgcHJvZHVjdHMgb3Igc2VydmljZXMgYmFzZWQgb24gYSB1c2VyJ3MNCiAgICAgICAgICAgICAgICAgICAgYnJvd3NpbmcgYW5kIHB1cmNoYXNlIGhpc3RvcnkNCiAgICAgICAgICAgICAgICAtICAgUGVyc29uYWxpemluZyB0aGUgc2hvcHBpbmcgZXhwZXJpZW5jZSBhbmQgaW5jcmVhc2luZw0KICAgICAgICAgICAgICAgICAgICBzYWxlcw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogQW1hem9uJ3MgIkN1c3RvbWVycyB3aG8gYm91Z2h0IHRoaXMgYWxzbw0KICAgICAgICAgICAgICAgICAgICBib3VnaHQiIGFuZCBOZXRmbGl4J3MgbW92aWUgcmVjb21tZW5kYXRpb25zDQogICAgICAgICAgICAyLiAgKipNZWRpYSBhbmQgRW50ZXJ0YWlubWVudCoqOg0KICAgICAgICAgICAgICAgIC0gICBTdWdnZXN0aW5nIG1vdmllcywgVFYgc2hvd3MsIG11c2ljLCBib29rcywgb3INCiAgICAgICAgICAgICAgICAgICAgYXJ0aWNsZXMgYmFzZWQgb24gdXNlciBwcmVmZXJlbmNlcw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgY29udGVudCBkaXNjb3ZlcnkgYW5kIGVuZ2FnZW1lbnQNCiAgICAgICAgICAgICAgICAtICAgRXhhbXBsZXM6IFlvdVR1YmUncyB2aWRlbyByZWNvbW1lbmRhdGlvbnMgYW5kDQogICAgICAgICAgICAgICAgICAgIFNwb3RpZnkncyBtdXNpYyBzdWdnZXN0aW9ucw0KICAgICAgICAgICAgMy4gICoqU29jaWFsIE1lZGlhIGFuZCBDb250ZW50IFBsYXRmb3JtcyoqOg0KICAgICAgICAgICAgICAgIC0gICBSZWNvbW1lbmRpbmcgcG9zdHMsIGFjY291bnRzLCBvciBjb21tdW5pdGllcyBiYXNlZA0KICAgICAgICAgICAgICAgICAgICBvbiB1c2VyIGludGVyZXN0cyBhbmQgc29jaWFsIGNvbm5lY3Rpb25zDQogICAgICAgICAgICAgICAgLSAgIEluY3JlYXNpbmcgdXNlciBlbmdhZ2VtZW50IGFuZCB0aW1lIHNwZW50IG9uIHRoZQ0KICAgICAgICAgICAgICAgICAgICBwbGF0Zm9ybQ0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogRmFjZWJvb2sncyBuZXdzIGZlZWQgcmVjb21tZW5kYXRpb25zIGFuZA0KICAgICAgICAgICAgICAgICAgICBUd2l0dGVyJ3MgIldobyB0byBGb2xsb3ciIHN1Z2dlc3Rpb25zDQogICAgICAgICAgICA0LiAgKipKb2IgYW5kIFRhbGVudCBNYXRjaGluZyoqOg0KICAgICAgICAgICAgICAgIC0gICBNYXRjaGluZyBqb2Igc2Vla2VycyB3aXRoIHJlbGV2YW50IGpvYiBwb3N0aW5ncw0KICAgICAgICAgICAgICAgICAgICBiYXNlZCBvbiB0aGVpciBza2lsbHMgYW5kIGV4cGVyaWVuY2UNCiAgICAgICAgICAgICAgICAtICAgSGVscGluZyBlbXBsb3llcnMgZmluZCB0aGUgYmVzdCBjYW5kaWRhdGVzIGZvciBvcGVuDQogICAgICAgICAgICAgICAgICAgIHBvc2l0aW9ucw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogTGlua2VkSW4ncyBqb2IgcmVjb21tZW5kYXRpb25zIGFuZA0KICAgICAgICAgICAgICAgICAgICByZWNydWl0bWVudCBwbGF0Zm9ybXMnIGNhbmRpZGF0ZSBtYXRjaGluZw0KICAgICAgICAgICAgNS4gICoqRmluYW5jaWFsIFNlcnZpY2VzKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgaW52ZXN0bWVudCBvcHBvcnR1bml0aWVzLCBmaW5hbmNpYWwNCiAgICAgICAgICAgICAgICAgICAgcHJvZHVjdHMsIG9yIHNlcnZpY2VzIGJhc2VkIG9uIGEgdXNlcidzIGZpbmFuY2lhbA0KICAgICAgICAgICAgICAgICAgICBwcm9maWxlIGFuZCBnb2Fscw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgZmluYW5jaWFsIHBsYW5uaW5nIGFuZCBkZWNpc2lvbi1tYWtpbmcNCiAgICAgICAgICAgICAgICAtICAgRXhhbXBsZXM6IFJvYm8tYWR2aXNvcnMnIGludmVzdG1lbnQgcmVjb21tZW5kYXRpb25zDQogICAgICAgICAgICAgICAgICAgIGFuZCBiYW5raW5nIGFwcHMnIHByb2R1Y3Qgc3VnZ2VzdGlvbnMNCiAgICAgICAgICAgIDYuICAqKkhlYWx0aGNhcmUgYW5kIFdlbGxuZXNzKio6DQogICAgICAgICAgICAgICAgLSAgIFJlY29tbWVuZGluZyB0cmVhdG1lbnRzLCBtZWRpY2F0aW9ucywgb3IgbGlmZXN0eWxlDQogICAgICAgICAgICAgICAgICAgIGNoYW5nZXMgYmFzZWQgb24gYSBwYXRpZW50J3MgbWVkaWNhbCBoaXN0b3J5IGFuZA0KICAgICAgICAgICAgICAgICAgICBzeW1wdG9tcw0KICAgICAgICAgICAgICAgIC0gICBJbXByb3ZpbmcgcGVyc29uYWxpemVkIGhlYWx0aGNhcmUgYW5kIHByb21vdGluZw0KICAgICAgICAgICAgICAgICAgICBoZWFsdGh5IGJlaGF2aW9ycw0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogVGVsZW1lZGljaW5lIHBsYXRmb3JtcycgdHJlYXRtZW50DQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyBhbmQgZml0bmVzcyBhcHBzJyB3b3Jrb3V0DQogICAgICAgICAgICAgICAgICAgIHN1Z2dlc3Rpb25zDQogICAgICAgICAgICA3LiAgKipFZHVjYXRpb24gYW5kIExlYXJuaW5nKio6DQogICAgICAgICAgICAgICAgLSAgIFN1Z2dlc3RpbmcgY291cnNlcywgbGVhcm5pbmcgbWF0ZXJpYWxzLCBvcg0KICAgICAgICAgICAgICAgICAgICBlZHVjYXRpb25hbCByZXNvdXJjZXMgYmFzZWQgb24gYSBzdHVkZW50J3MgaW50ZXJlc3RzDQogICAgICAgICAgICAgICAgICAgIGFuZCBwZXJmb3JtYW5jZQ0KICAgICAgICAgICAgICAgIC0gICBFbmhhbmNpbmcgdGhlIGxlYXJuaW5nIGV4cGVyaWVuY2UgYW5kIHN1cHBvcnRpbmcNCiAgICAgICAgICAgICAgICAgICAgcGVyc29uYWxpemVkIGVkdWNhdGlvbg0KICAgICAgICAgICAgICAgIC0gICBFeGFtcGxlczogT25saW5lIGxlYXJuaW5nIHBsYXRmb3JtcycgY291cnNlDQogICAgICAgICAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucyBhbmQgZWR1Y2F0aW9uYWwgYXBwcycgY29udGVudA0KICAgICAgICAgICAgICAgICAgICBzdWdnZXN0aW9ucw0KDQotICAgUmVjb21tZW5kZXIgc3lzdGVtcyBjYW4gYmUgcmVwcmVzZW50ZWQgbWF0aGVtYXRpY2FsbHkgdXNpbmcgdGhlDQogICAgZm9sbG93aW5nIGtleSBjb21wb25lbnRzOg0KDQogICAgMS4gICoqVXNlcnMqKjogTGV0IFUgPSB7dTEsIHUyLCAuLi4sIHVtfSBiZSB0aGUgc2V0IG9mIG0gdXNlcnMuDQoNCiAgICAyLiAgKipJdGVtcyoqOiBMZXQgSSA9IHtpMSwgaTIsIC4uLiwgaW59IGJlIHRoZSBzZXQgb2YgbiBpdGVtcy4NCg0KICAgIDMuICAqKlVzZXItSXRlbSBJbnRlcmFjdGlvbnMqKjogTGV0IFIgYmUgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbg0KICAgICAgICBtYXRyaXgsIHdoZXJlIFJbdSwgaV0gcmVwcmVzZW50cyB0aGUgcmF0aW5nLCBwcmVmZXJlbmNlLCBvcg0KICAgICAgICBpbnRlcmFjdGlvbiBvZiB1c2VyIHUgd2l0aCBpdGVtIGkuDQoNCiAgICAgICAgLSAgIFIgY2FuIGJlIGEgc3BhcnNlIG1hdHJpeCwgYXMgdXNlcnMgdHlwaWNhbGx5IGludGVyYWN0IHdpdGgNCiAgICAgICAgICAgIG9ubHkgYSBzbWFsbCBzdWJzZXQgb2YgYWxsIGF2YWlsYWJsZSBpdGVtcy4NCiAgICAgICAgLSAgIFIgY2FuIGNvbnRhaW4gZXhwbGljaXQgcmF0aW5ncyAoZS5nLiwgMS01IHN0YXJzKSBvciBpbXBsaWNpdA0KICAgICAgICAgICAgaW50ZXJhY3Rpb25zIChlLmcuLCBwdXJjaGFzZXMsIHZpZXdzLCBjbGlja3MpLg0KDQogICAgNC4gICoqUHJlZGljdGlvbiBGdW5jdGlvbioqOiBUaGUgZ29hbCBvZiBhIHJlY29tbWVuZGVyIHN5c3RlbSBpcyB0bw0KICAgICAgICBsZWFybiBhIHByZWRpY3Rpb24gZnVuY3Rpb24gZjogVSDDlyBJIOKGkiBSIHRoYXQgZXN0aW1hdGVzIHRoZQ0KICAgICAgICBwcmVmZXJlbmNlIG9yIHJhdGluZyBvZiBhIHVzZXIgdSBmb3IgYW4gaXRlbSBpLg0KDQogICAgICAgIC0gICBUaGlzIGZ1bmN0aW9uIGNhbiBiZSBsZWFybmVkIHVzaW5nIHZhcmlvdXMgbWFjaGluZSBsZWFybmluZw0KICAgICAgICAgICAgdGVjaG5pcXVlcywgc3VjaCBhcyBtYXRyaXggZmFjdG9yaXphdGlvbiwgZGVlcCBsZWFybmluZywgb3INCiAgICAgICAgICAgIGh5YnJpZCBhcHByb2FjaGVzLg0KDQogICAgNS4gICoqUmVjb21tZW5kYXRpb24gR2VuZXJhdGlvbioqOiBHaXZlbiBhIHVzZXIgdSwgdGhlIHJlY29tbWVuZGVyDQogICAgICAgIHN5c3RlbSBnZW5lcmF0ZXMgYSByYW5rZWQgbGlzdCBvZiBpdGVtcyBpIOKIiCBJIHRoYXQgdGhlIHVzZXIgaXMNCiAgICAgICAgbW9zdCBsaWtlbHkgdG8gaW50ZXJhY3Qgd2l0aCBvciBwcmVmZXIsIGJhc2VkIG9uIHRoZSBsZWFybmVkDQogICAgICAgIHByZWRpY3Rpb24gZnVuY3Rpb24gZi4NCg0KICAgIDYuICAqKkV2YWx1YXRpb24gTWV0cmljcyoqOiBSZWNvbW1lbmRlciBzeXN0ZW1zIGFyZSB0eXBpY2FsbHkNCiAgICAgICAgZXZhbHVhdGVkIHVzaW5nIG1ldHJpY3Mgc3VjaCBhczoNCg0KICAgICAgICAtICAgW1ByZWNpc2lvblxAa10obWFpbHRvOlByZWNpc2lvbkBrKXsuZW1haWx9OiBUaGUgZnJhY3Rpb24gb2YNCiAgICAgICAgICAgIHRoZSB0b3AtayByZWNvbW1lbmRlZCBpdGVtcyB0aGF0IGFyZSByZWxldmFudCB0byB0aGUgdXNlci4NCiAgICAgICAgLSAgIFtSZWNhbGxcQGtdKG1haWx0bzpSZWNhbGxAayl7LmVtYWlsfTogVGhlIGZyYWN0aW9uIG9mDQogICAgICAgICAgICByZWxldmFudCBpdGVtcyB0aGF0IGFyZSBpbmNsdWRlZCBpbiB0aGUgdG9wLWsNCiAgICAgICAgICAgIHJlY29tbWVuZGF0aW9ucy4NCiAgICAgICAgLSAgIE5vcm1hbGl6ZWQgRGlzY291bnRlZCBDdW11bGF0aXZlIEdhaW4gKE5EQ0cpOiBBIG1lYXN1cmUgb2YNCiAgICAgICAgICAgIHJhbmtpbmcgcXVhbGl0eSB0aGF0IGNvbnNpZGVycyB0aGUgcG9zaXRpb24gb2YgcmVsZXZhbnQNCiAgICAgICAgICAgIGl0ZW1zIGluIHRoZSByZWNvbW1lbmRhdGlvbiBsaXN0Lg0KICAgICAgICAtICAgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIG9yIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKToNCiAgICAgICAgICAgIE1lYXN1cmVzIHRoZSBhY2N1cmFjeSBvZiByYXRpbmcgcHJlZGljdGlvbnMuDQoNCiAgICBUaGlzIG1hdGhlbWF0aWNhbCByZXByZXNlbnRhdGlvbiBwcm92aWRlcyBhIGZyYW1ld29yayBmb3INCiAgICB1bmRlcnN0YW5kaW5nIHRoZSBjb3JlIGNvbXBvbmVudHMgYW5kIG9iamVjdGl2ZXMgb2YgcmVjb21tZW5kZXINCiAgICBzeXN0ZW1zLCB3aGljaCBjYW4gdGhlbiBiZSBpbXBsZW1lbnRlZCB1c2luZyB2YXJpb3VzIGFsZ29yaXRobXMgYW5kDQogICAgdGVjaG5pcXVlcy4NCg0KLSAgIA0KDQogICAgIyMgUmVjb21tZW5kZXIgU3lzdGVtczogTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uDQoNCiAgICAxLiAgKipVc2VycyBhbmQgSXRlbXMqKjoNCiAgICAgICAgLSAgIExldCBVID0ge3UxLCB1MiwgLi4uLCB1bX0gYmUgdGhlIHNldCBvZiBtIHVzZXJzLg0KICAgICAgICAtICAgTGV0IEkgPSB7aTEsIGkyLCAuLi4sIGlufSBiZSB0aGUgc2V0IG9mIG4gaXRlbXMuDQogICAgMi4gICoqVXNlci1JdGVtIEludGVyYWN0aW9ucyoqOg0KICAgICAgICAtICAgTGV0IFIgYmUgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbiBtYXRyaXgsIHdoZXJlIFJbdSwgaV0NCiAgICAgICAgICAgIHJlcHJlc2VudHMgdGhlIHJhdGluZywgcHJlZmVyZW5jZSwgb3IgaW50ZXJhY3Rpb24gb2YgdXNlciB1DQogICAgICAgICAgICB3aXRoIGl0ZW0gaS4NCiAgICAgICAgLSAgIFIgaXMgdHlwaWNhbGx5IGEgc3BhcnNlIG1hdHJpeCwgYXMgdXNlcnMgaW50ZXJhY3Qgd2l0aCBvbmx5DQogICAgICAgICAgICBhIHNtYWxsIHN1YnNldCBvZiBhbGwgYXZhaWxhYmxlIGl0ZW1zLg0KICAgICAgICAtICAgUiBjYW4gY29udGFpbiBleHBsaWNpdCByYXRpbmdzIChlLmcuLCAxLTUgc3RhcnMpIG9yIGltcGxpY2l0DQogICAgICAgICAgICBpbnRlcmFjdGlvbnMgKGUuZy4sIHB1cmNoYXNlcywgdmlld3MsIGNsaWNrcykuDQogICAgMy4gICoqUHJlZGljdGlvbiBGdW5jdGlvbioqOg0KICAgICAgICAtICAgVGhlIGdvYWwgaXMgdG8gbGVhcm4gYSBwcmVkaWN0aW9uIGZ1bmN0aW9uIGY6IFUgw5cgSSDihpIgUiB0aGF0DQogICAgICAgICAgICBlc3RpbWF0ZXMgdGhlIHByZWZlcmVuY2Ugb3IgcmF0aW5nIG9mIGEgdXNlciB1IGZvciBhbiBpdGVtDQogICAgICAgICAgICBpLg0KICAgICAgICAtICAgVGhpcyBmdW5jdGlvbiBjYW4gYmUgbGVhcm5lZCB1c2luZyB2YXJpb3VzIG1hY2hpbmUgbGVhcm5pbmcNCiAgICAgICAgICAgIHRlY2huaXF1ZXMsIHN1Y2ggYXMgbWF0cml4IGZhY3Rvcml6YXRpb24sIGRlZXAgbGVhcm5pbmcsIG9yDQogICAgICAgICAgICBoeWJyaWQgYXBwcm9hY2hlcy4NCiAgICA0LiAgKipSZWNvbW1lbmRhdGlvbiBHZW5lcmF0aW9uKio6DQogICAgICAgIC0gICBHaXZlbiBhIHVzZXIgdSwgdGhlIHJlY29tbWVuZGVyIHN5c3RlbSBnZW5lcmF0ZXMgYSByYW5rZWQNCiAgICAgICAgICAgIGxpc3Qgb2YgaXRlbXMgaSDiiIggSSB0aGF0IHRoZSB1c2VyIGlzIG1vc3QgbGlrZWx5IHRvIGludGVyYWN0DQogICAgICAgICAgICB3aXRoIG9yIHByZWZlciwgYmFzZWQgb24gdGhlIGxlYXJuZWQgcHJlZGljdGlvbiBmdW5jdGlvbiBmLg0KICAgIDUuICAqKkV2YWx1YXRpb24gTWV0cmljcyoqOg0KICAgICAgICAtICAgW1ByZWNpc2lvblxAa10obWFpbHRvOlByZWNpc2lvbkBrKXsuZW1haWx9LA0KICAgICAgICAgICAgW1JlY2FsbFxAa10obWFpbHRvOlJlY2FsbEBrKXsuZW1haWx9LCBOb3JtYWxpemVkIERpc2NvdW50ZWQNCiAgICAgICAgICAgIEN1bXVsYXRpdmUgR2FpbiAoTkRDRyksIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSwgUm9vdCBNZWFuDQogICAgICAgICAgICBTcXVhcmVkIEVycm9yIChSTVNFKS4NCg0KICAgICMjIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaW4gTWFjaGluZSBMZWFybmluZw0KDQogICAgU1ZEIGlzIGEgcG93ZXJmdWwgbWF0cml4IGZhY3Rvcml6YXRpb24gdGVjaG5pcXVlIHRoYXQgY2FuIGJlIHVzZWQNCiAgICBmb3IgdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIHRhc2tzLCBpbmNsdWRpbmcgcmVjb21tZW5kZXIgc3lzdGVtcy4NCg0KICAgICoqRXhhbXBsZTogU1ZEIGZvciBDb2xsYWJvcmF0aXZlIEZpbHRlcmluZyBpbiBSZWNvbW1lbmRlciBTeXN0ZW1zKioNCg0KICAgIDEuICAqKlVzZXItSXRlbSBJbnRlcmFjdGlvbiBNYXRyaXgqKjoNCiAgICAgICAgLSAgIExldCBSIGJlIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4LCB3aGVyZSBSW3UsIGldDQogICAgICAgICAgICByZXByZXNlbnRzIHRoZSByYXRpbmcgb3IgcHJlZmVyZW5jZSBvZiB1c2VyIHUgZm9yIGl0ZW0gaS4NCiAgICAyLiAgKipTVkQgRGVjb21wb3NpdGlvbioqOg0KICAgICAgICAtICAgRGVjb21wb3NlIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4IFIgaW50byB0aHJlZQ0KICAgICAgICAgICAgbWF0cmljZXM6IFUsIM6jLCBhbmQgVlxeVC4NCiAgICAgICAgLSAgIFIgPSBVzqNWXF5ULCB3aGVyZToNCiAgICAgICAgICAgIC0gICBVIGlzIGFuIG0gw5cgbSBvcnRob2dvbmFsIG1hdHJpeCByZXByZXNlbnRpbmcgdGhlIGxlZnQNCiAgICAgICAgICAgICAgICBzaW5ndWxhciB2ZWN0b3JzLg0KICAgICAgICAgICAgLSAgIM6jIGlzIGFuIG0gw5cgbiBkaWFnb25hbCBtYXRyaXggY29udGFpbmluZyB0aGUgc2luZ3VsYXINCiAgICAgICAgICAgICAgICB2YWx1ZXMuDQogICAgICAgICAgICAtICAgVlxeVCBpcyBhbiBuIMOXIG4gb3J0aG9nb25hbCBtYXRyaXggcmVwcmVzZW50aW5nIHRoZQ0KICAgICAgICAgICAgICAgIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMuDQogICAgMy4gICoqUmVjb21tZW5kYXRpb24gR2VuZXJhdGlvbioqOg0KICAgICAgICAtICAgVG8gcHJlZGljdCB0aGUgcmF0aW5nIG9yIHByZWZlcmVuY2Ugb2YgYSB1c2VyIHUgZm9yIGFuIGl0ZW0NCiAgICAgICAgICAgIGksIHVzZSB0aGUgZm9sbG93aW5nIGZvcm11bGE6DQogICAgICAgICAgICAtICAgUlt1LCBpXSDiiYggKFUgzqMgVlxeVClbdSwgaV0NCiAgICAgICAgLSAgIFRoZSB0b3AtayBpdGVtcyB3aXRoIHRoZSBoaWdoZXN0IHByZWRpY3RlZCByYXRpbmdzIGNhbiBiZQ0KICAgICAgICAgICAgcmVjb21tZW5kZWQgdG8gdGhlIHVzZXIuDQogICAgNC4gICoqQWR2YW50YWdlcyBvZiBTVkQqKjoNCiAgICAgICAgLSAgIEhhbmRsZXMgdGhlIHNwYXJzaXR5IG9mIHRoZSB1c2VyLWl0ZW0gaW50ZXJhY3Rpb24gbWF0cml4Lg0KICAgICAgICAtICAgQ2FwdHVyZXMgdGhlIGxhdGVudCBmYWN0b3JzIG9yIGhpZGRlbiBmZWF0dXJlcyB0aGF0DQogICAgICAgICAgICBpbmZsdWVuY2UgdXNlciBwcmVmZXJlbmNlcy4NCiAgICAgICAgLSAgIFByb3ZpZGVzIGEgbG93LXJhbmsgYXBwcm94aW1hdGlvbiBvZiB0aGUgb3JpZ2luYWwgbWF0cml4LA0KICAgICAgICAgICAgd2hpY2ggY2FuIGltcHJvdmUgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5Lg0KICAgICAgICAtICAgQ2FuIGJlIGNvbWJpbmVkIHdpdGggb3RoZXIgdGVjaG5pcXVlcywgc3VjaCBhcw0KICAgICAgICAgICAgcmVndWxhcml6YXRpb24sIHRvIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZQ0KICAgICAgICAgICAgcmVjb21tZW5kZXIgc3lzdGVtLg0KDQogICAgKipFeGFtcGxlOiBTVkQgZm9yIEltYWdlIENvbXByZXNzaW9uKioNCg0KICAgIDEuICAqKkltYWdlIFJlcHJlc2VudGF0aW9uKio6DQogICAgICAgIC0gICBMZXQgWCBiZSB0aGUgbSDDlyBuIGltYWdlIG1hdHJpeCwgd2hlcmUgZWFjaCBlbGVtZW50DQogICAgICAgICAgICByZXByZXNlbnRzIHRoZSBwaXhlbCBpbnRlbnNpdHkuDQogICAgMi4gICoqU1ZEIERlY29tcG9zaXRpb24qKjoNCiAgICAgICAgLSAgIERlY29tcG9zZSB0aGUgaW1hZ2UgbWF0cml4IFggaW50byB0aHJlZSBtYXRyaWNlczogVSwgzqMsIGFuZA0KICAgICAgICAgICAgVlxeVC4NCiAgICAgICAgLSAgIFggPSBVzqNWXF5ULCB3aGVyZToNCiAgICAgICAgICAgIC0gICBVIGlzIGFuIG0gw5cgbSBvcnRob2dvbmFsIG1hdHJpeCByZXByZXNlbnRpbmcgdGhlIGxlZnQNCiAgICAgICAgICAgICAgICBzaW5ndWxhciB2ZWN0b3JzLg0KICAgICAgICAgICAgLSAgIM6jIGlzIGFuIG0gw5cgbiBkaWFnb25hbCBtYXRyaXggY29udGFpbmluZyB0aGUgc2luZ3VsYXINCiAgICAgICAgICAgICAgICB2YWx1ZXMuDQogICAgICAgICAgICAtICAgVlxeVCBpcyBhbiBuIMOXIG4gb3J0aG9nb25hbCBtYXRyaXggcmVwcmVzZW50aW5nIHRoZQ0KICAgICAgICAgICAgICAgIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMuDQogICAgMy4gICoqSW1hZ2UgQ29tcHJlc3Npb24qKjoNCiAgICAgICAgLSAgIFJldGFpbiBvbmx5IHRoZSBrIGxhcmdlc3Qgc2luZ3VsYXIgdmFsdWVzIGluIM6jIGFuZCB0aGUNCiAgICAgICAgICAgIGNvcnJlc3BvbmRpbmcgY29sdW1ucyBpbiBVIGFuZCBWXF5ULg0KICAgICAgICAtICAgVGhlIGNvbXByZXNzZWQgaW1hZ2UgY2FuIGJlIHJlY29uc3RydWN0ZWQgYXMgWF9jb21wcmVzc2VkID0NCiAgICAgICAgICAgIFVfayDOo19rIFZfa1xeVCwgd2hlcmUgdGhlIHN1YnNjcmlwdCBrIGluZGljYXRlcyB0aGUNCiAgICAgICAgICAgIHJlZHVjZWQtcmFuayBtYXRyaWNlcy4NCiAgICA0LiAgKipBZHZhbnRhZ2VzIG9mIFNWRCBmb3IgSW1hZ2UgQ29tcHJlc3Npb24qKjoNCiAgICAgICAgLSAgIFByb3ZpZGVzIGEgbG93LXJhbmsgYXBwcm94aW1hdGlvbiBvZiB0aGUgb3JpZ2luYWwgaW1hZ2UsDQogICAgICAgICAgICByZWR1Y2luZyB0aGUgc3RvcmFnZSBhbmQgdHJhbnNtaXNzaW9uIHJlcXVpcmVtZW50cy4NCiAgICAgICAgLSAgIFByZXNlcnZlcyB0aGUgbW9zdCBpbXBvcnRhbnQgZmVhdHVyZXMgYW5kIHN0cnVjdHVyZXMgb2YgdGhlDQogICAgICAgICAgICBpbWFnZSwgcmVzdWx0aW5nIGluIGhpZ2gtcXVhbGl0eSByZWNvbnN0cnVjdGlvbnMuDQogICAgICAgIC0gICBDYW4gYmUgdXNlZCBmb3IgdmFyaW91cyBpbWFnZSBwcm9jZXNzaW5nIHRhc2tzLCBzdWNoIGFzDQogICAgICAgICAgICBkZW5vaXNpbmcsIGZlYXR1cmUgZXh0cmFjdGlvbiwgYW5kIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4NCg0KICAgIFNWRCBpcyBhIHZlcnNhdGlsZSB0ZWNobmlxdWUgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBhIHdpZGUgcmFuZ2Ugb2YNCiAgICBtYWNoaW5lIGxlYXJuaW5nIHByb2JsZW1zLCBpbmNsdWRpbmcgcmVjb21tZW5kZXIgc3lzdGVtcywgaW1hZ2UNCiAgICBwcm9jZXNzaW5nLCBhbmQgZGF0YSBhbmFseXNpcy4gVW5kZXJzdGFuZGluZyB0aGUgbWF0aGVtYXRpY2FsDQogICAgcmVwcmVzZW50YXRpb24gYW5kIGV4YW1wbGVzIG9mIFNWRCBpcyBjcnVjaWFsIGZvciBkZXZlbG9waW5nDQogICAgZWZmZWN0aXZlIGFuZCBlZmZpY2llbnQgbWFjaGluZSBsZWFybmluZyBzb2x1dGlvbnMuDQoNCi0gICANCg0KICAgICMgU3R1ZHkgR3VpZGU6IFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24gKFNWRCkgaW4gTWFjaGluZSBMZWFybmluZw0KDQogICAgIyMgSW50cm9kdWN0aW9uIHRvIFNWRA0KDQogICAgU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSBpcyBhIG1hdHJpeCBmYWN0b3JpemF0aW9uDQogICAgdGVjaG5pcXVlIHVzZWQgaW4gdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIGFwcGxpY2F0aW9ucywgaW5jbHVkaW5nDQogICAgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLCBub2lzZSByZWR1Y3Rpb24sIGFuZCBjb2xsYWJvcmF0aXZlDQogICAgZmlsdGVyaW5nIGluIHJlY29tbWVuZGVyIHN5c3RlbXMuDQoNCiAgICAjIyMgTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uDQoNCiAgICBHaXZlbiBhIG1hdHJpeCAkQSQgb2Ygc2l6ZSAkbSBcdGltZXMgbiQsIFNWRCBkZWNvbXBvc2VzICRBJCBpbnRvDQogICAgdGhyZWUgbWF0cmljZXM6DQoNCiAgICAkJCBBID0gVSBcU2lnbWEgVl5UICQkDQoNCiAgICAtICAgJFUkIGlzIGFuICRtIFx0aW1lcyBtJCBvcnRob2dvbmFsIG1hdHJpeC4gVGhlIGNvbHVtbnMgb2YgJFUkIGFyZQ0KICAgICAgICB0aGUgbGVmdCBzaW5ndWxhciB2ZWN0b3JzIG9mICRBJC4NCiAgICAtICAgJFxTaWdtYSQgaXMgYW4gJG0gXHRpbWVzIG4kIGRpYWdvbmFsIG1hdHJpeCB3aXRoIG5vbi1uZWdhdGl2ZQ0KICAgICAgICByZWFsIG51bWJlcnMgb24gdGhlIGRpYWdvbmFsLiBUaGVzZSBudW1iZXJzIGFyZSB0aGUgc2luZ3VsYXINCiAgICAgICAgdmFsdWVzIG9mICRBJC4NCiAgICAtICAgJFZeVCQgaXMgdGhlIHRyYW5zcG9zZSBvZiBhbiAkbiBcdGltZXMgbiQgb3J0aG9nb25hbCBtYXRyaXguIFRoZQ0KICAgICAgICBjb2x1bW5zIG9mICRWJCBhcmUgdGhlIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMgb2YgJEEkLg0KDQogICAgIyMjIFByb3BlcnRpZXMNCg0KICAgIC0gICBUaGUgc2luZ3VsYXIgdmFsdWVzIGluICRcU2lnbWEkIGFyZSBzb3J0ZWQgaW4gZGVzY2VuZGluZyBvcmRlci4NCiAgICAtICAgVGhlIG51bWJlciBvZiBub24temVybyBzaW5ndWxhciB2YWx1ZXMgaXMgZXF1YWwgdG8gdGhlIHJhbmsgb2YNCiAgICAgICAgdGhlIG1hdHJpeCAkQSQuDQoNCiAgICAjIyBBcHBsaWNhdGlvbnMgb2YgU1ZEIGluIE1hY2hpbmUgTGVhcm5pbmcNCg0KICAgICMjIyAxLiBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24NCg0KICAgIC0gICAqKlByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkqKjogU1ZEIGlzIHVzZWQgdG8gY29tcHV0ZQ0KICAgICAgICB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgYSBkYXRhc2V0LCB3aGljaCBhcmUgdGhlIGRpcmVjdGlvbnMNCiAgICAgICAgb2YgbWF4aW11bSB2YXJpYW5jZS4gQnkgcHJvamVjdGluZyBkYXRhIG9udG8gdGhlIGZpcnN0IGZldw0KICAgICAgICBwcmluY2lwYWwgY29tcG9uZW50cywgd2UgY2FuIHJlZHVjZSB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlDQogICAgICAgIGRhdGEgd2hpbGUgcHJlc2VydmluZyBtb3N0IG9mIGl0cyB2YXJpYW5jZS4NCg0KICAgICMjIyAyLiBSZWNvbW1lbmRlciBTeXN0ZW1zDQoNCiAgICAtICAgKipDb2xsYWJvcmF0aXZlIEZpbHRlcmluZyoqOiBJbiByZWNvbW1lbmRlciBzeXN0ZW1zLCBTVkQgaXMgdXNlZA0KICAgICAgICB0byBkZWNvbXBvc2UgdGhlIHVzZXItaXRlbSBpbnRlcmFjdGlvbiBtYXRyaXggaW50byBsYXRlbnQNCiAgICAgICAgZmFjdG9ycy4gVGhpcyBoZWxwcyBpbiBwcmVkaWN0aW5nIG1pc3NpbmcgZW50cmllcyAoZS5nLiwNCiAgICAgICAgcmF0aW5ncykgYnkgcmVjb25zdHJ1Y3RpbmcgdGhlIG1hdHJpeCB1c2luZyBhIHJlZHVjZWQgbnVtYmVyIG9mDQogICAgICAgIHNpbmd1bGFyIHZhbHVlcy4NCg0KICAgICMjIyMgRXhhbXBsZTogTW92aWUgUmVjb21tZW5kYXRpb24NCg0KICAgIDEuICAqKlVzZXItSXRlbSBNYXRyaXgqKjogQ29uc2lkZXIgYSBtYXRyaXggJFIkIHdoZXJlIHJvd3MgcmVwcmVzZW50DQogICAgICAgIHVzZXJzIGFuZCBjb2x1bW5zIHJlcHJlc2VudCBtb3ZpZXMuIEVhY2ggZW50cnkgJFJbdSwgaV0kIGlzIHRoZQ0KICAgICAgICByYXRpbmcgZ2l2ZW4gYnkgdXNlciAkdSQgdG8gbW92aWUgJGkkLg0KDQogICAgMi4gICoqU1ZEIERlY29tcG9zaXRpb24qKjogRGVjb21wb3NlICRSJCB1c2luZyBTVkQ6DQoNCiAgICAgICAgJCQgUiBcYXBwcm94IFVfayBcU2lnbWFfayBWX2teVCAkJA0KDQogICAgICAgIEhlcmUsICRVX2skLCAkXFNpZ21hX2skLCBhbmQgJFZfa15UJCBhcmUgdHJ1bmNhdGVkIG1hdHJpY2VzDQogICAgICAgIGNvbnRhaW5pbmcgb25seSB0aGUgdG9wICRrJCBzaW5ndWxhciB2YWx1ZXMgYW5kIGNvcnJlc3BvbmRpbmcNCiAgICAgICAgdmVjdG9ycy4NCg0KICAgIDMuICAqKlByZWRpY3Rpb24qKjogUHJlZGljdCB0aGUgcmF0aW5nIGZvciBhIHVzZXItbW92aWUgcGFpciBieQ0KICAgICAgICByZWNvbnN0cnVjdGluZyB0aGUgbWF0cml4Og0KDQogICAgICAgICQkIFxoYXR7Un0gPSBVX2sgXFNpZ21hX2sgVl9rXlQgJCQNCg0KICAgICAgICBUaGUgcHJlZGljdGVkIHJhdGluZyBmb3IgdXNlciAkdSQgYW5kIG1vdmllICRpJCBpcw0KICAgICAgICAkXGhhdHtSfVt1LCBpXSQuDQoNCiAgICAjIyMgMy4gTm9pc2UgUmVkdWN0aW9uDQoNCiAgICAtICAgKipJbWFnZSBDb21wcmVzc2lvbioqOiBTVkQgY2FuIGJlIHVzZWQgdG8gY29tcHJlc3MgaW1hZ2VzIGJ5DQogICAgICAgIGtlZXBpbmcgb25seSB0aGUgbGFyZ2VzdCBzaW5ndWxhciB2YWx1ZXMsIHdoaWNoIGNhcHR1cmUgdGhlIG1vc3QNCiAgICAgICAgc2lnbmlmaWNhbnQgZmVhdHVyZXMgb2YgdGhlIGltYWdlLCB3aGlsZSBkaXNjYXJkaW5nIHNtYWxsZXINCiAgICAgICAgc2luZ3VsYXIgdmFsdWVzIHRoYXQgcmVwcmVzZW50IG5vaXNlLg0KDQogICAgIyMgUHJhY3RpY2FsIENvbnNpZGVyYXRpb25zDQoNCiAgICAtICAgKipDaG9vc2luZyoqICRrJDogVGhlIGNob2ljZSBvZiAkayQgKG51bWJlciBvZiBzaW5ndWxhciB2YWx1ZXMNCiAgICAgICAgdG8ga2VlcCkgaXMgY3J1Y2lhbC4gQSBzbWFsbGVyICRrJCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5IGJ1dA0KICAgICAgICBtYXkgbG9zZSBpbXBvcnRhbnQgaW5mb3JtYXRpb24sIHdoaWxlIGEgbGFyZ2VyICRrJCByZXRhaW5zIG1vcmUNCiAgICAgICAgaW5mb3JtYXRpb24gYnV0IG1heSBpbmNsdWRlIG5vaXNlLg0KICAgIC0gICAqKkNvbXB1dGF0aW9uYWwgQ29tcGxleGl0eSoqOiBTVkQgY2FuIGJlIGNvbXB1dGF0aW9uYWxseQ0KICAgICAgICBleHBlbnNpdmUgZm9yIGxhcmdlIG1hdHJpY2VzLiBFZmZpY2llbnQgYWxnb3JpdGhtcyBhbmQNCiAgICAgICAgYXBwcm94aW1hdGlvbnMgKGUuZy4sIHRydW5jYXRlZCBTVkQpIGFyZSBvZnRlbiB1c2VkIGluIHByYWN0aWNlLg0KDQogICAgIyMgQ29uY2x1c2lvbg0KDQogICAgU1ZEIGlzIGEgcG93ZXJmdWwgdG9vbCBpbiBtYWNoaW5lIGxlYXJuaW5nIGZvciB0YXNrcyBpbnZvbHZpbmcNCiAgICBtYXRyaXggZmFjdG9yaXphdGlvbi4gSXRzIGFiaWxpdHkgdG8gZGVjb21wb3NlIG1hdHJpY2VzIGludG8NCiAgICBtZWFuaW5nZnVsIGNvbXBvbmVudHMgbWFrZXMgaXQgaW52YWx1YWJsZSBmb3IgYXBwbGljYXRpb25zIGxpa2UNCiAgICBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24sIGNvbGxhYm9yYXRpdmUgZmlsdGVyaW5nLCBhbmQgbm9pc2UNCiAgICByZWR1Y3Rpb24uDQoNCi0gICANCg0KICAgICMgTWFjaGluZSBMZWFybmluZyBSZWNvbW1lbmRhdGlvbiBTeXN0ZW1zOiBTdHVkeSBHdWlkZQ0KDQogICAgIyMgSS4gQ29yZSBSZWNvbW1lbmRhdGlvbiBTeXN0ZW0gVHlwZXMNCg0KICAgICMjIyBBLiBDb2xsYWJvcmF0aXZlIEZpbHRlcmluZw0KDQogICAgMS4gICoqVXNlci1CYXNlZCAoVUJDRikqKg0KICAgICAgICAtICAgTWF0aGVtYXRpY2FsIHJlcHJlc2VudGF0aW9uOg0KDQogICAgICAgICAgICBgYGAgICAgICAgICANCiAgICAgICAgICAgIHByZWQodSxpKSA9IG1lYW4ocmF0aW5nc191KSArIM6jKHNpbSh1LHYpIMOXIChyYXRpbmdfdixpIC0gbWVhbihyYXRpbmdzX3YpKSkNCiAgICAgICAgICAgIHdoZXJlOg0KICAgICAgICAgICAgLSBwcmVkKHUsaSkgaXMgdGhlIHByZWRpY3Rpb24gZm9yIHVzZXIgdSBvbiBpdGVtIGkNCiAgICAgICAgICAgIC0gc2ltKHUsdikgaXMgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB1c2VycyB1IGFuZCB2DQogICAgICAgICAgICBgYGANCiAgICAyLiAgKipJdGVtLUJhc2VkIChJQkNGKSoqDQogICAgICAgIC0gICBNYXRoZW1hdGljYWwgcmVwcmVzZW50YXRpb246DQoNCiAgICAgICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICAgICAgcHJlZCh1LGkpID0gzqMoc2ltKGksaikgw5cgcmF0aW5nX3UsaikgLyDOo3xzaW0oaSxqKXwNCiAgICAgICAgICAgIHdoZXJlOg0KICAgICAgICAgICAgLSBzaW0oaSxqKSBpcyB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIGl0ZW1zIGkgYW5kIGoNCiAgICAgICAgICAgIGBgYA0KICAgIDMuICAqKk1vZGVsLUJhc2VkKioNCiAgICAgICAgLSAgIFVzZXMgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgdG8gcHJlZGljdCByYXRpbmdzDQoNCiAgICAgICAgLSAgIENvbW1vbiBhcHByb2FjaDogTWF0cml4IEZhY3Rvcml6YXRpb24NCg0KICAgICAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgICAgICBSIOKJiCBQIMOXIFFeVA0KICAgICAgICAgICAgd2hlcmU6DQogICAgICAgICAgICAtIFIgaXMgdGhlIHVzZXItaXRlbSByYXRpbmcgbWF0cml4DQogICAgICAgICAgICAtIFAgaXMgdGhlIHVzZXIgbGF0ZW50IGZhY3RvciBtYXRyaXgNCiAgICAgICAgICAgIC0gUSBpcyB0aGUgaXRlbSBsYXRlbnQgZmFjdG9yIG1hdHJpeA0KICAgICAgICAgICAgYGBgDQoNCiAgICAjIyMgQi4gQ29udGVudC1CYXNlZCBGaWx0ZXJpbmcNCg0KICAgIDEuICAqKkZlYXR1cmUgRXh0cmFjdGlvbioqDQogICAgICAgIC0gICBUZXh0OiBURi1JREYgcmVwcmVzZW50YXRpb24NCg0KICAgICAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgICAgICBURi1JREYodCxkKSA9IFRGKHQsZCkgw5cgSURGKHQpDQogICAgICAgICAgICB3aGVyZToNCiAgICAgICAgICAgIC0gVEYodCxkKSBpcyB0ZXJtIGZyZXF1ZW5jeQ0KICAgICAgICAgICAgLSBJREYodCkgaXMgaW52ZXJzZSBkb2N1bWVudCBmcmVxdWVuY3kNCiAgICAgICAgICAgIGBgYA0KDQogICAgICAgIC0gICBJbWFnZXM6IENOTiBmZWF0dXJlcw0KICAgIDIuICAqKlByb2ZpbGUgTGVhcm5pbmcqKg0KICAgICAgICAtICAgTWV0aG9kczoNCiAgICAgICAgICAgIC0gICBEZWNpc2lvbiBUcmVlcw0KICAgICAgICAgICAgLSAgIE5haXZlIEJheWVzDQogICAgICAgICAgICAtICAgTmV1cmFsIE5ldHdvcmtzDQogICAgICAgICAgICAtICAgU1ZNDQogICAgICAgICAgICAtICAgUmVncmVzc2lvbiBNb2RlbHMNCg0KICAgICMjIElJLiBBZHZhbmNlZCBUZWNobmlxdWVzDQoNCiAgICAjIyMgQS4gTWF0cml4IEZhY3Rvcml6YXRpb24NCg0KICAgIDEuICAqKlNWRCAoU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbikqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBBID0gVSDOoyBWXlQNCiAgICAgICAgd2hlcmU6DQogICAgICAgIC0gQSBpcyB0aGUgb3JpZ2luYWwgbWF0cml4DQogICAgICAgIC0gVSBjb250YWlucyBsZWZ0IHNpbmd1bGFyIHZlY3RvcnMNCiAgICAgICAgLSDOoyBjb250YWlucyBzaW5ndWxhciB2YWx1ZXMNCiAgICAgICAgLSBWXlQgY29udGFpbnMgcmlnaHQgc2luZ3VsYXIgdmVjdG9ycw0KICAgICAgICBgYGANCg0KICAgIDIuICAqKkFMUyAoQWx0ZXJuYXRpbmcgTGVhc3QgU3F1YXJlcykqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBNaW5pbWl6ZTogzqMocl91aSAtIHBfdV5UIHFfaSleMiArIM67KHx8cF91fHxeMiArIHx8cV9pfHxeMikNCiAgICAgICAgd2hlcmU6DQogICAgICAgIC0gcl91aSBpcyB0aGUgcmF0aW5nIG9mIHVzZXIgdSBmb3IgaXRlbSBpDQogICAgICAgIC0gcF91IGlzIHRoZSB1c2VyIGxhdGVudCBmYWN0b3INCiAgICAgICAgLSBxX2kgaXMgdGhlIGl0ZW0gbGF0ZW50IGZhY3Rvcg0KICAgICAgICAtIM67IGlzIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXINCiAgICAgICAgYGBgDQoNCiAgICAjIyMgQi4gRGVlcCBMZWFybmluZyBBcHByb2FjaGVzDQoNCiAgICAxLiAgTmV1cmFsIENvbGxhYm9yYXRpdmUgRmlsdGVyaW5nDQogICAgMi4gIEF1dG9lbmNvZGVycw0KICAgIDMuICBSTk5zIGZvciBTZXF1ZW50aWFsIFJlY29tbWVuZGF0aW9ucw0KICAgIDQuICBDTk5zIGZvciBGZWF0dXJlIExlYXJuaW5nDQoNCiAgICAjIyBJSUkuIEV2YWx1YXRpb24gTWV0cmljcw0KDQogICAgMS4gICoqQWNjdXJhY3kgTWV0cmljcyoqDQoNCiAgICAgICAgYGBgICAgICAgICAgDQogICAgICAgIFJNU0UgPSDiiJoozqMoeV90cnVlIC0geV9wcmVkKV4yIC8gbikNCiAgICAgICAgTUFFID0gzqN8eV90cnVlIC0geV9wcmVkfCAvIG4NCiAgICAgICAgYGBgDQoNCiAgICAyLiAgKipSYW5raW5nIE1ldHJpY3MqKg0KDQogICAgICAgIGBgYCAgICAgICAgIA0KICAgICAgICBQcmVjaXNpb25AayA9IHJlbGV2YW50X2l0ZW1zQGsgLyBrDQogICAgICAgIFJlY2FsbEBrID0gcmVsZXZhbnRfaXRlbXNAayAvIHRvdGFsX3JlbGV2YW50X2l0ZW1zDQogICAgICAgIE5EQ0dAayA9IERDR0BrIC8gSURDR0BrDQogICAgICAgIGBgYA0KDQogICAgIyMgSVYuIEltcGxlbWVudGF0aW9uIENvbnNpZGVyYXRpb25zDQoNCiAgICAxLiAgKipDb2xkIFN0YXJ0IFByb2JsZW0qKg0KICAgICAgICAtICAgU29sdXRpb25zOg0KICAgICAgICAgICAgLSAgIEh5YnJpZCBhcHByb2FjaGVzDQogICAgICAgICAgICAtICAgQ29udGVudC1iYXNlZCBpbml0aWFsaXphdGlvbg0KICAgICAgICAgICAgLSAgIERlZmF1bHQgcmVjb21tZW5kYXRpb25zDQogICAgMi4gICoqU2NhbGFiaWxpdHkqKg0KICAgICAgICAtICAgVGVjaG5pcXVlczoNCiAgICAgICAgICAgIC0gICBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24NCiAgICAgICAgICAgIC0gICBTYW1wbGluZw0KICAgICAgICAgICAgLSAgIERpc3RyaWJ1dGVkIGNvbXB1dGluZw0KICAgIDMuICAqKlJlYWwtdGltZSBVcGRhdGVzKioNCiAgICAgICAgLSAgIE9ubGluZSBsZWFybmluZw0KICAgICAgICAtICAgSW5jcmVtZW50YWwgdXBkYXRlcw0KICAgICAgICAtICAgU3RyZWFtIHByb2Nlc3NpbmcNCg0KICAgICMjIFYuIEZ1dHVyZSBUcmVuZHMNCg0KICAgIDEuICBEZWVwIExlYXJuaW5nIEludGVncmF0aW9uDQogICAgMi4gIFJlaW5mb3JjZW1lbnQgTGVhcm5pbmcNCiAgICAzLiAgR3JhcGggTmV1cmFsIE5ldHdvcmtzDQogICAgNC4gIE5hdHVyYWwgTGFuZ3VhZ2UgUHJvY2Vzc2luZw0KICAgIDUuICBGZWRlcmF0ZWQgTGVhcm5pbmcNCiAgICA2LiAgRXhwbGFpbmFibGUgQUkNCg0KICAgICMjIFZJLiBCZW5lZml0cyBhbmQgQXBwbGljYXRpb25zDQoNCiAgICAxLiAgUGVyc29uYWxpemVkIENvbnRlbnQgRGVsaXZlcnkNCiAgICAyLiAgSW5jcmVhc2VkIFVzZXIgRW5nYWdlbWVudA0KICAgIDMuICBSZXZlbnVlIEdyb3d0aA0KICAgIDQuICBJbXByb3ZlZCBVc2VyIEV4cGVyaWVuY2UNCiAgICA1LiAgQXV0b21hdGVkIERlY2lzaW9uIE1ha2luZw0KICAgIDYuICBTY2FsYWJsZSBTb2x1dGlvbnMNCg0KW3N2ZA0Kc3R1ZHldKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9yZWNvbW1lbmRlci1zeXN0ZW1zLWEtY29tcGxldGUtZ3VpZGUtdG8tbWFjaGluZS1sZWFybmluZy1tb2RlbHMtOTZkM2Y5NGVhNzQ4KQ0KDQpbYW5vdGhlciBnb29kDQpsaW5rXShodHRwczovL2luc2lnaHRzLmRhZmZvZGlsc3cuY29tL2Jsb2cvbWFjaGluZS1sZWFybmluZy1hbGdvcml0aG1zLWZvci1yZWNvbW1lbmRhdGlvbi1lbmdpbmVzKQ0K