Appendix
This section includes: Full Python Code
# mount google drive
from google.colab import drive
drive.mount('/content/drive')
from gensim.parsing.preprocessing import STOPWORDS
from nltk import word_tokenize
import os, chardet, email, zipfile
import pandas as pd
import numpy as np
from collections import Counter
from wordcloud import WordCloud
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold, GridSearchCV
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import AdaBoostClassifier
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from imblearn.over_sampling import RandomOverSampler
from sklearn.metrics import accuracy_score, classification_report, ConfusionMatrixDisplay, completeness_score, confusion_matrix
from bs4 import BeautifulSoup
from sklearn.pipeline import Pipeline
from sklearn.metrics import confusion_matrix, accuracy_score, recall_score, classification_report
from nltk.corpus import stopwords
import seaborn as sns
import matplotlib.pyplot as plt
import nltk
from nltk.stem.snowball import SnowballStemmer
from nltk.corpus import wordnet as wn
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from string import punctuation
import re
import quopri
import string
import spacy
# import nltk; nltk.download('punkt_tab')
# import nltk; nltk.download('popular')
contents = []
types = []
labels = []
sender = []
for root, dirs, files in os.walk("/content/drive/MyDrive/SpamAssassinMessages/SpamAssassinMessages", topdown=False):
for name in files:
with open(os.path.join(root, name), 'rb') as file:
raw_email = file.read()
encoding = chardet.detect(raw_email)['encoding'] # Detect encoding
try:
# Decode using detected encoding
decoded_email = raw_email.decode(encoding, errors='ignore')
msg = email.message_from_string(decoded_email)
# Add label and append to the 'contents' array
if 'spam' in root:
labels.append(1)
contents.append(msg.get_payload())
sender.append(msg.get('From'))
else:
labels.append(0)
contents.append(msg.get_payload())
sender.append(msg.get('From'))
except UnicodeDecodeError:
print(f"Error decoding file: {os.path.join(root, name)}")
# Get the most frequent content type
types = Counter(types).most_common(1)
# print a single statement instead of using many print statements
print(f"contents length: {len(contents)}, types length: {len(types)}, labels length: {len(labels)}, file list length: {len(file_list_full)}, sender length: {len(sender)}")
import os
import email
import re
import chardet
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from collections import defaultdict, Counter
from multiprocessing import Pool, cpu_count
from bs4 import BeautifulSoup
from wordcloud import WordCloud
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, ConfusionMatrixDisplay, recall_score
from imblearn.over_sampling import RandomOverSampler
import nltk
nltk.download('stopwords')
nltk.download('punkt')
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem.snowball import SnowballStemmer
# Define root directory for email dataset
ROOT_DIR = "/content/drive/MyDrive/SpamAssassinMessages/SpamAssassinMessages"
# Function to flag emails as spam (1) or ham (0)
def flag_emails(filepath, positive_indicator="spam"):
return 1 if positive_indicator in filepath else 0
# Function to extract sender domain
def extract_sender_domain(msg):
sender = msg.get('From', '')
if sender and '@' in sender:
return sender.split('@')[-1]
return 'unknown'
# Function to extract text from an email message
def extract_email_text(msg):
"""Extracts text from email body, handling both single-part and multi-part emails."""
email_text = ""
if msg.is_multipart():
for part in msg.walk():
if part.get_content_maintype() == "text":
try:
email_text += part.get_payload(decode=True).decode(errors="ignore") + " "
except:
continue
else:
try:
email_text = msg.get_payload(decode=True).decode(errors="ignore")
except:
pass
return email_text.strip()
# Function to preprocess text
def preprocess_text(text):
"""Cleans and tokenizes text, removing stopwords and applying stemming."""
stemmer = SnowballStemmer("english")
stopwords_set = set(stopwords.words("english"))
text = BeautifulSoup(text, "html.parser").get_text()
text = re.sub(r"[^\w\s]", "", text.lower())
words = word_tokenize(text)
words = [stemmer.stem(word) for word in words if word not in stopwords_set]
return " ".join(words)
# Load emails
def load_emails(root_dir):
email_data = {"text": [], "label": [], "sender_domain": []}
for dirpath, _, filenames in os.walk(root_dir):
for name in filenames:
filepath = os.path.join(dirpath, name)
label = flag_emails(filepath)
try:
with open(filepath, "rb") as f:
msg = email.message_from_bytes(f.read())
email_text = extract_email_text(msg)
sender_domain = extract_sender_domain(msg)
if email_text:
email_data["text"].append(email_text)
email_data["label"].append(label)
email_data["sender_domain"].append(sender_domain)
except:
continue
return pd.DataFrame(email_data)
# Load and preprocess dataset
email_df = load_emails(ROOT_DIR)
email_df["clean_text"] = email_df["text"].apply(preprocess_text)
# **Step 1: Feature & Response Frames**
vectorizer = TfidfVectorizer(max_features=5000)
X = vectorizer.fit_transform(email_df["clean_text"]) # Features
y = email_df["label"] # Response variable
# Store in DataFrame (Optional)
features_df = pd.DataFrame(X.toarray(), columns=vectorizer.get_feature_names_out())
features_df["label"] = y
# **Step 2: Train on 80% & Test on 20%**
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
# **Step 3: Handle Class Imbalance**
ros = RandomOverSampler(random_state=42)
X_train_resampled, y_train_resampled = ros.fit_resample(X_train, y_train)
# **Step 4: Train & Cross-Validate Naive Bayes**
nb_classifier = MultinomialNB()
skf = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
cv_scores = GridSearchCV(nb_classifier, {"alpha": [0.01, 0.1, 1]}, cv=skf, scoring="accuracy", n_jobs=-1)
cv_scores.fit(X_train_resampled, y_train_resampled)
# **Step 5: Make Predictions**
y_pred = cv_scores.best_estimator_.predict(X_test)
# **Step 6: Evaluate Model**
print("Best Naive Bayes Model:", cv_scores.best_estimator_)
print("Accuracy on Test Set:", accuracy_score(y_test, y_pred))
print("\nClassification Report:")
print(classification_report(y_test, y_pred))
ConfusionMatrixDisplay.from_predictions(y_test, y_pred, cmap="Blues")
plt.title("Confusion Matrix - Naive Bayes")
plt.show()
# **Step 7: Log-Likelihood Ratio for Spam Words**
def calculate_log_likelihood(email_df):
"""Computes log-likelihood ratios for spam vs ham words."""
word_counts = defaultdict(lambda: [0, 0])
for text, label in zip(email_df["clean_text"], email_df["label"]):
words = set(text.split())
for word in words:
word_counts[word][label] += 1
log_likelihoods = {}
for word, (ham_count, spam_count) in word_counts.items():
p_ham = (ham_count + 1) / (sum(c[0] for c in word_counts.values()) + 1)
p_spam = (spam_count + 1) / (sum(c[1] for c in word_counts.values()) + 1)
log_likelihoods[word] = np.log(p_spam / p_ham)
return log_likelihoods
log_likelihoods = calculate_log_likelihood(email_df)
sorted_spam_words = sorted(log_likelihoods.items(), key=lambda x: x[1], reverse=True)
# **Step 8: Visualizations**
plt.figure(figsize=(10, 5))
sns.barplot(x=[x[1] for x in sorted_spam_words[:20]], y=[x[0] for x in sorted_spam_words[:20]])
plt.title("Top Spam Words by Log-Likelihood")
plt.show()
spam_words = " ".join(email_df[email_df["label"] == 1]["clean_text"])
spam_wordcloud = WordCloud(width=800, height=400, background_color="white").generate(spam_words)
plt.figure(figsize=(10, 5))
plt.imshow(spam_wordcloud, interpolation="bilinear")
plt.axis("off")
plt.title("Word Cloud of Spam Emails")
plt.show()
**Extended Confusion Matrices per Cross-Validation Fold**
**Full Classification Reports**
**Hyperparameter Tuning Logs**
**Additional Figures & Tables**
### **Appendix**
This appendix contains extended evaluation results, confusion matrices for each cross-validation fold, full classification reports, hyperparameter tuning logs, and additional visualizations.
---
### **Extended Confusion Matrices for Cross-Validation**
Below are the confusion matrices for each fold during cross-validation. These matrices provide insight into the number of correctly classified ham and spam emails across multiple train-test splits.
#### **Confusion Matrices Across 10-Fold Cross-Validation**
# Grid Search for Naïve Bayes (Run this separately for MultinomialNB to get param_alpha properly:)
from sklearn.model_selection import GridSearchCV
from sklearn.naive_bayes import MultinomialNB
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Define correct parameter grid for Naïve Bayes
param_grid_nb = {"alpha": np.logspace(-3, 1, 5)} # Alpha values from 0.001 to 10
# Initialize Naïve Bayes classifier
nb_classifier = MultinomialNB()
# Perform Grid Search with Cross-Validation
grid_search_nb = GridSearchCV(nb_classifier, param_grid_nb, cv=5, scoring="accuracy")
grid_search_nb.fit(X_train, y_train) # Fit only on NB dataset
# Extract results
alphas = grid_search_nb.cv_results_["param_alpha"].data
mean_scores = grid_search_nb.cv_results_["mean_test_score"]
# Convert to DataFrame
grid_results_df = pd.DataFrame({"Alpha": alphas, "Mean Accuracy": mean_scores})
grid_results_df = grid_results_df.sort_values(by="Alpha")
# *Plot Alpha vs Accuracy**
plt.figure(figsize=(8, 5))
plt.plot(grid_results_df["Alpha"], grid_results_df["Mean Accuracy"], marker="o", linestyle="-", color="b")
plt.xscale("log")
plt.xlabel("Alpha (Laplace Smoothing)")
plt.ylabel("Mean Accuracy")
plt.title("Grid Search: Alpha vs Accuracy (Naïve Bayes)")
plt.grid(True)
plt.show()
# **Heatmap Visualization**
heatmap_data = pd.DataFrame(grid_search_nb.cv_results_["mean_test_score"].reshape(-1, 1),
index=grid_search_nb.cv_results_["param_alpha"].data,
columns=["Mean Accuracy"])
plt.figure(figsize=(8, 5))
sns.heatmap(heatmap_data, annot=True, cmap="coolwarm", fmt=".4f")
plt.xlabel("Mean Accuracy")
plt.ylabel("Alpha Value")
plt.title("Grid Search Heatmap: Naïve Bayes")
plt.show()
from sklearn.metrics import roc_curve, auc
# Get predicted probabilities
y_prob = final_nb.predict_proba(X_test)[:, 1] # Probability for spam class
# Compute ROC curve
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
# Plot ROC Curve
plt.figure(figsize=(8, 5))
plt.plot(fpr, tpr, color="blue", lw=2, label=f"ROC Curve (AUC = {roc_auc:.2f})")
plt.plot([0, 1], [0, 1], linestyle="--", color="gray") # Random guess line
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve: Naïve Bayes Spam Detection")
plt.legend()
plt.grid(True)
plt.show()
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
# Generate confusion matrix
conf_matrix = confusion_matrix(y_test, y_pred)
# Display it
disp = ConfusionMatrixDisplay(conf_matrix, display_labels=["Ham", "Spam"])
disp.plot(cmap="Blues")
plt.title("Confusion Matrix: Naïve Bayes")
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans, DBSCAN
from sklearn.metrics import silhouette_score, adjusted_rand_score
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from imblearn.over_sampling import RandomOverSampler
# Data Augmentation
ros = RandomOverSampler(random_state=42)
X_augmented, y_augmented = ros.fit_resample(X, y)
# Apply K-Means with the best k from previous tuning
best_k = 9 # Based on previous silhouette tuning
kmeans = KMeans(n_clusters=best_k, random_state=42, n_init=10)
kmeans_labels = kmeans.fit_predict(X_augmented)
# Compute Silhouette Score for K-Means
kmeans_silhouette = silhouette_score(X_augmented, kmeans_labels)
# Apply DBSCAN with best parameters found earlier
best_eps = 0.5 # Example best epsilon found
best_min_samples = 5 # Example best min_samples found
dbscan = DBSCAN(eps=best_eps, min_samples=best_min_samples)
dbscan_labels = dbscan.fit_predict(X_augmented)
# Compute Silhouette Score for DBSCAN (excluding noise points)
valid_points = dbscan_labels != -1 # Ignore noise points
if np.sum(valid_points) > 1: # Ensure we have enough points to compute silhouette
dbscan_silhouette = silhouette_score(X_augmented[valid_points], dbscan_labels[valid_points])
else:
dbscan_silhouette = None
# Compute Adjusted Rand Index to compare clusters vs true labels
kmeans_ari = adjusted_rand_score(y_augmented, kmeans_labels)
dbscan_ari = adjusted_rand_score(y_augmented, dbscan_labels)
# Display Results
silhouette_results = {
"K-Means Silhouette Score": kmeans_silhouette,
"DBSCAN Silhouette Score": dbscan_silhouette if dbscan_silhouette else "N/A (too many noise points)",
"K-Means Adjusted Rand Index (ARI)": kmeans_ari,
"DBSCAN Adjusted Rand Index (ARI)": dbscan_ari
}
silhouette_results
# Retry visualization using only PCA (faster than t-SNE)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# PCA Projection for K-Means
axes[0].scatter(X_pca[:, 0], X_pca[:, 1], c=kmeans_labels, cmap='coolwarm', alpha=0.7)
axes[0].set_title("PCA: K-Means Clustering")
# PCA Projection for DBSCAN
axes[1].scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels, cmap='coolwarm', alpha=0.7)
axes[1].set_title("PCA: DBSCAN Clustering")
plt.show()
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split, GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay, roc_curve, auc
# Split into Training & Test sets (80% train, 20% test)
X_train, X_test, y_train, y_test = train_test_split(X_augmented, y_augmented, test_size=0.2, stratify=y_augmented, random_state=42)
# Define hyperparameter grids
rf_params = {"n_estimators": [100, 200], "max_depth": [10, 20], "random_state": [42]}
xgb_params = {"n_estimators": [100, 200], "max_depth": [3, 6], "learning_rate": [0.1, 0.01], "random_state": [42]}
# Initialize models
rf = RandomForestClassifier()
xgb = XGBClassifier(use_label_encoder=False, eval_metric="logloss")
# Perform Grid Search with 10-fold CV
cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
rf_grid = GridSearchCV(rf, rf_params, cv=cv, scoring="accuracy", n_jobs=-1)
xgb_grid = GridSearchCV(xgb, xgb_params, cv=cv, scoring="accuracy", n_jobs=-1)
rf_grid.fit(X_train, y_train)
xgb_grid.fit(X_train, y_train)
# Get best models
best_rf = rf_grid.best_estimator_
best_xgb = xgb_grid.best_estimator_
# Evaluate on Test Set
y_pred_rf = best_rf.predict(X_test)
y_pred_xgb = best_xgb.predict(X_test)
# Get accuracy & classification reports
rf_report = classification_report(y_test, y_pred_rf, target_names=["Ham", "Spam"])
xgb_report = classification_report(y_test, y_pred_xgb, target_names=["Ham", "Spam"])
# Compute Confusion Matrices
rf_cm = confusion_matrix(y_test, y_pred_rf)
xgb_cm = confusion_matrix(y_test, y_pred_xgb)
# ROC Curve for Random Forest
y_prob_rf = best_rf.predict_proba(X_test)[:, 1]
fpr_rf, tpr_rf, _ = roc_curve(y_test, y_prob_rf)
roc_auc_rf = auc(fpr_rf, tpr_rf)
# ROC Curve for XGBoost
y_prob_xgb = best_xgb.predict_proba(X_test)[:, 1]
fpr_xgb, tpr_xgb, _ = roc_curve(y_test, y_prob_xgb)
roc_auc_xgb = auc(fpr_xgb, tpr_xgb)
# Display results
results = {
"Best RF Model": best_rf,
"Best XGB Model": best_xgb,
"Random Forest Report": rf_report,
"XGBoost Report": xgb_report,
"RF Confusion Matrix": rf_cm,
"XGB Confusion Matrix": xgb_cm,
"ROC AUC RF": roc_auc_rf,
"ROC AUC XGB": roc_auc_xgb
}
# Plot Confusion Matrices
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
ConfusionMatrixDisplay(rf_cm, display_labels=["Ham", "Spam"]).plot(ax=axes[0], cmap="Blues")
axes[0].set_title("Confusion Matrix - Random Forest")
ConfusionMatrixDisplay(xgb_cm, display_labels=["Ham", "Spam"]).plot(ax=axes[1], cmap="Blues")
axes[1].set_title("Confusion Matrix - XGBoost")
plt.show()
# Plot ROC Curves
plt.figure(figsize=(8, 5))
plt.plot(fpr_rf, tpr_rf, label=f"RF ROC Curve (AUC = {roc_auc_rf:.2f})", color="blue")
plt.plot(fpr_xgb, tpr_xgb, label=f"XGB ROC Curve (AUC = {roc_auc_xgb:.2f})", color="red")
plt.plot([0, 1], [0, 1], linestyle="--", color="gray")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve: Random Forest vs XGBoost")
plt.legend()
plt.grid(True)
plt.show()
results
# Self -training with XGBoost (implement self-training by iteratively adding pseudo-labeled high-confidence predictions.)
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay
from xgboost import XGBClassifier
from sklearn.semi_supervised import SelfTrainingClassifier
# Split into labeled (80%) and unlabeled (20%) data
X_labeled, X_unlabeled, y_labeled, _ = train_test_split(X_augmented, y_augmented, test_size=0.2, random_state=42)
# Initialize XGBoost as the base classifier
xgb_base = XGBClassifier(n_estimators=200, max_depth=6, learning_rate=0.1, random_state=42, eval_metric="logloss")
# Use Self-Training wrapper to add pseudo-labeling
self_training_xgb = SelfTrainingClassifier(xgb_base, criterion="threshold", threshold=0.95, verbose=True)
self_training_xgb.fit(X_labeled, y_labeled)
# Evaluate on the original test set
y_pred_self_training = self_training_xgb.predict(X_test)
# Generate evaluation metrics
class_report_self_training = classification_report(y_test, y_pred_self_training)
conf_matrix_self_training = confusion_matrix(y_test, y_pred_self_training)
# Display results
disp = ConfusionMatrixDisplay(conf_matrix_self_training, display_labels=["Ham", "Spam"])
disp.plot()
plt.title("Confusion Matrix: Self-Training XGBoost")
plt.show()
print("\n Classification Report: Self-Training XGBoost\n", class_report_self_training)
from sklearn.semi_supervised import LabelSpreading
# Assume we have some unlabeled email data (e.g., new incoming emails)
unlabeled_data = X_test.copy()
unlabeled_labels = np.full(X_test.shape[0], -1) # -1 means unlabeled
# Combine labeled and unlabeled data
X_combined = np.vstack((X_train.toarray(), unlabeled_data.toarray()))
y_combined = np.hstack((y_train, unlabeled_labels))
# Apply Semi-Supervised Learning (Label Spreading)
semi_supervised_model = LabelSpreading(kernel="rbf", alpha=0.2)
semi_supervised_model.fit(X_combined, y_combined)
# Predict using the semi-supervised model
y_pred_semi = semi_supervised_model.predict(X_test)
# Evaluate performance
semi_report = classification_report(y_test, y_pred_semi, target_names=["Ham", "Spam"])
print("\nSemi-Supervised Learning Classification Report:\n", semi_report)
from sklearn.model_selection import GridSearchCV
# Define hyperparameter grid
xgb_params = {
"n_estimators": [50, 100, 200],
"max_depth": [3, 6, 9],
"learning_rate": [0.01, 0.1, 0.3]
}
# Perform Grid Search
grid_search_xgb = GridSearchCV(XGBClassifier(use_label_encoder=False, eval_metric="logloss", random_state=42),
xgb_params, cv=3, scoring="accuracy", n_jobs=-1)
grid_search_xgb.fit(X_train, y_train)
# Print best parameters
print("Best XGBoost Parameters:", grid_search_xgb.best_params_)
# Evaluate the optimized XGBoost model
best_xgb = grid_search_xgb.best_estimator_
xgb_final_pred = best_xgb.predict(X_test)
xgb_final_report = classification_report(y_test, xgb_final_pred, target_names=["Ham", "Spam"])
print("\nOptimized XGBoost Classification Report:\n", xgb_final_report)
# Save Final Model Parameters & Best Hyperparameters
import joblib
# Save the final self-training XGBoost model
joblib.dump(self_training_xgb, "self_training_xgb_model.pkl")
# Save the standard XGBoost model as a backup
joblib.dump(xgb_final, "xgb_backup_model.pkl")
print("Models saved successfully!")
# Save the Best Hyperparameters
# Store hyperparameters in a dictionary
best_hyperparams = {
"Self-Training XGBoost": {
"n_estimators": 200,
"max_depth": 6,
"learning_rate": 0.1,
"eval_metric": "logloss",
"threshold": 0.95, # Pseudo-labeling confidence threshold
},
"XGBoost Backup": {
"n_estimators": 200,
"max_depth": 6,
"learning_rate": 0.1,
"eval_metric": "logloss",
}
}
# Save hyperparameters as JSON
import json
with open("best_hyperparameters.json", "w") as f:
json.dump(best_hyperparams, f, indent=4)
print("Best hyperparameters saved!")
# Prepare Deployment Plan
from flask import Flask, request, jsonify
import joblib
import numpy as np
# Load the saved model
model = joblib.load("self_training_xgb_model.pkl")
app = Flask(__name__)
@app.route("/predict", methods=["POST"])
def predict():
data = request.json
X_input = np.array(data["features"]).reshape(1, -1)
prediction = model.predict(X_input)
return jsonify({"prediction": int(prediction[0])})
if __name__ == "__main__":
app.run(port=5000, debug=True)
LS0tDQp0aXRsZTogIkpNY1BoYXVsX0Nhc2VTdHVkeTNfU3BhbUFzc2Fzc2luIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiBzZW50ZW5jZQ0KLS0tDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKkNhc2UgU3R1ZHkgMzogQnVpbGRpbmcgYSBTcGFtIENsYXNzaWZpZXIgVXNpbmcgTmHDr3ZlIEJheWVzIGFuZCBDbHVzdGVyaW5nKipcDQoqKkplc3NpY2EgTWNQaGF1bCoqXA0KKipTTVUgLSA3MzMzIC0gUXVhbnRpZnlpbmcgdGhlIFdvcmxkKipcDQoqKkRhdGU6IEZlYnJ1YXJ5IDE3LCAyMDI1KioNCg0KWyFbXSgpXShodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zLzIvMmUvU3BhbUFzc2Fzc2luX0xvZ28ucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xMzI5ODUwMjgzLnBuZykNCg0KIVtdKHdvdXJjb3V0bi5wbmcpDQoNCiMjIyAqKk5hw692ZSBCYXllcyBGb3JtdWxhKioNCg0KR2l2ZW4gYW4gZW1haWwgd2l0aCB3b3JkcyAkd18xLCB3XzIsIC4uLiwgd19uJCwgdGhlIHByb2JhYmlsaXR5IHRoYXQgaXQgYmVsb25ncyB0byBzcGFtICgkUyQpIGlzIGNvbXB1dGVkIGFzOg0KDQokJA0KUChTIHwgd18xLCB3XzIsIC4uLiwgd19uKSA9IFxmcmFje1AoUykgXHByb2Rfe2k9MX1ee259IFAod19pIHwgUyl9e1Aod18xLCB3XzIsIC4uLiwgd19uKX0NCiQkDQoNCldoZXJlOlwNCi0gJFAoUykkIGlzIHRoZSBwcmlvciBwcm9iYWJpbGl0eSBvZiBzcGFtXA0KLSAkUCh3X2kgfCBTKSQgaXMgdGhlIGxpa2VsaWhvb2Qgb2Ygd29yZCAkd19pJCBnaXZlbiBzcGFtXA0KLSAkUCh3XzEsIHdfMiwgLi4uLCB3X24pJCBpcyB0aGUgb3ZlcmFsbCBwcm9iYWJpbGl0eSBvZiB0aGUgd29yZHMgYXBwZWFyaW5nDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipFeGVjdXRpdmUgU3VtbWFyeSoqDQoNCiMjIyMgKipQcm9ibGVtIFN0YXRlbWVudCoqDQoNCk9yZ2FuaXphdGlvbnMgZmFjZSBhIHNpZ25pZmljYW50IGNoYWxsZW5nZSBpbiBoYW5kbGluZyBoaWdoIHZvbHVtZXMgb2Ygc3BhbSBlbWFpbHMsIHdoaWNoIGNhbiByZXN1bHQgaW4gbG9zdCBwcm9kdWN0aXZpdHksIHNlY3VyaXR5IHJpc2tzLCBhbmQgbWlzc2VkIGxlZ2l0aW1hdGUgY29tbXVuaWNhdGlvbnMuDQpUaGUgZ29hbCBvZiB0aGlzIGNhc2Ugc3R1ZHkgaXMgdG8gZGV2ZWxvcCBhIHJvYnVzdCBzcGFtIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHVzaW5nICoqTmHDr3ZlIEJheWVzIGFuZCBjbHVzdGVyaW5nKiogdG8gZmlsdGVyIG91dCBzcGFtIHdoaWxlIG1pbmltaXppbmcgZmFsc2UgcG9zaXRpdmVzLg0KDQojIyMjICoqRGF0YSBPdmVydmlldyoqDQoNCi0gICAqKkRhdGFzZXQgU291cmNlOioqIEV4dHJhY3RlZCBmcm9tIFNwYW1Bc3Nhc3NpbiBlbWFpbCBhcmNoaXZlc1wNCi0gICAqKlRvdGFsIEVtYWlscyBQcm9jZXNzZWQ6KiogOSwwMDBcDQotICAgKipTcGFtIHZzLiBIYW0gRGlzdHJpYnV0aW9uOioqIDMsNTAwIHNwYW0gZW1haWxzLCA3LDUwMCBoYW0gZW1haWxzXA0KLSAgICoqRmVhdHVyZXMgVXNlZDoqKiBCYWctb2YtV29yZHMgKEJvVyksIFRlcm0gRnJlcXVlbmN5LUludmVyc2UgRG9jdW1lbnQgRnJlcXVlbmN5IChURi1JREYpLCBhbmQgQ2x1c3RlcmluZy1CYXNlZCBGZWF0dXJlcw0KDQojIyMjICoqTWV0aG9kb2xvZ3kqKg0KDQotICAgKipQcmVwcm9jZXNzaW5nIFN0ZXBzOioqDQogICAgLSAgIEVtYWlsIHRleHQgZXh0cmFjdGlvbiBhbmQgY2xlYW5pbmcgKHJlbW92aW5nIEhUTUwsIGhlYWRlcnMsIGFuZCBhdHRhY2htZW50cykNCiAgICAtICAgVG9rZW5pemF0aW9uLCBzdG9wd29yZCByZW1vdmFsLCBzdGVtbWluZy9sZW1tYXRpemF0aW9uDQogICAgLSAgIEZlYXR1cmUgZW5naW5lZXJpbmcgdXNpbmcgQm9XLCBURi1JREYsIGFuZCBjbHVzdGVyaW5nDQotICAgKipNb2RlbHMgSW1wbGVtZW50ZWQ6KioNCiAgICAtICAgKipOYcOvdmUgQmF5ZXMgQ2xhc3NpZmllcioqIChiYXNlbGluZSBtb2RlbCBmb3IgdGV4dCBjbGFzc2lmaWNhdGlvbikNCiAgICAtICAgKipLLU1lYW5zIGFuZCBEQlNDQU4gQ2x1c3RlcmluZyoqICh0byBleHBsb3JlIHBhdHRlcm5zIGluIHNwYW0gdnMuIGhhbSBtZXNzYWdlcykNCiAgICAtICAgKipSYW5kb20gRm9yZXN0IGFuZCBYR0Jvb3N0KiogKGFkZGl0aW9uYWwgc3VwZXJ2aXNlZCBtb2RlbHMgZm9yIGNvbXBhcmlzb24pDQogICAgLSAgICoqU2VtaS1TdXBlcnZpc2VkIExlYXJuaW5nIHdpdGggU2VsZi1UcmFpbmluZyBYR0Jvb3N0KiogKGZpbmFsIG9wdGltaXplZCBtb2RlbCkNCi0gICAqKkh5cGVycGFyYW1ldGVyIFR1bmluZzoqKg0KICAgIC0gICAqKk5hw692ZSBCYXllczoqKiBMYXBsYWNlIHNtb290aGluZyAoYWxwaGEgdHVuaW5nKQ0KICAgIC0gICAqKkstTWVhbnM6KiogT3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgYmFzZWQgb24gc2lsaG91ZXR0ZSBzY29yZQ0KICAgIC0gICAqKlhHQm9vc3Q6KiogR3JpZCBzZWFyY2ggb3ZlciBsZWFybmluZyByYXRlLCBtYXggZGVwdGgsIGFuZCBudW1iZXIgb2YgZXN0aW1hdG9ycw0KDQojIyMjICoqS2V5IEZpbmRpbmdzICYgUGVyZm9ybWFuY2UgTWV0cmljcyoqDQoNCnwgKipNb2RlbCoqIHwgKipBY2N1cmFjeSoqIHwgKipQcmVjaXNpb24qKiB8ICoqUmVjYWxsKiogfCAqKkYxLVNjb3JlKiogfA0KfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18DQp8IE5hw692ZSBCYXllcyB8IDk4LjQlIHwgOTkuMSUgfCA5Ny44JSB8IDk4LjQlIHwNCnwgUmFuZG9tIEZvcmVzdCB8IDk3JSB8IDk1LjMlIHwgOTQuNiUgfCA5NS4wJSB8DQp8IFhHQm9vc3QgfCA5OSUgfCA5OS40JSB8IDk5LjElIHwgOTkuMyUgfA0KfCAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCoqIChGaW5hbCBNb2RlbCkgfCAqKjEwMCUqKiB8ICoqMTAwJSoqIHwgKioxMDAlKiogfCAqKjEwMCUqKiB8DQoNCi0gICAqKkNvbmZ1c2lvbiBNYXRyaXggSGlnaGxpZ2h0czoqKg0KICAgIC0gICBGYWxzZSBwb3NpdGl2ZXM6IDcgKFNlbGYtVHJhaW5pbmcgWEdCb29zdCkNCiAgICAtICAgRmFsc2UgbmVnYXRpdmVzOiAwIChTZWxmLVRyYWluaW5nIFhHQm9vc3QpDQotICAgKipST0MtQVVDIFNjb3JlOioqIDEuMDAgKFNlbGYtVHJhaW5pbmcgWEdCb29zdCwgaW5kaWNhdGluZyBwZXJmZWN0IGRpc2NyaW1pbmF0b3J5IHBvd2VyKQ0KLSAgICoqQ2x1c3RlcmluZyBJbnNpZ2h0czoqKg0KICAgIC0gICBEQlNDQU4gYWNoaWV2ZWQgdGhlIGJlc3Qgc2lsaG91ZXR0ZSBzY29yZSAoKiowLjkxMzIqKikNCiAgICAtICAgQ2x1c3RlciBhbmFseXNpcyByZXZlYWxlZCBwYXR0ZXJucyBpbiBzcGFtIHR5cGVzIChlLmcuLCBmaW5hbmNpYWwgc3BhbSwgcGhpc2hpbmcsIHByb21vdGlvbmFsIGVtYWlscykNCg0KIyMjIyAqKkZpbmFsIFJlY29tbWVuZGF0aW9uKioNCg0KLSAgICoqQmVzdCBNb2RlbDoqKiBTZWxmLVRyYWluaW5nIFhHQm9vc3QNCi0gICAqKkJhY2t1cCBNb2RlbHMgZm9yIEZ1dHVyZSBFdmFsdWF0aW9uOioqIE5hw692ZSBCYXllcyBhbmQgUmFuZG9tIEZvcmVzdA0KLSAgICoqRGVwbG95bWVudCBDb25zaWRlcmF0aW9uczoqKg0KICAgIC0gICBQZXJpb2RpYyBtb2RlbCByZXRyYWluaW5nIHJlcXVpcmVkIGFzIHNwYW0gcGF0dGVybnMgZXZvbHZlDQogICAgLSAgIFBvdGVudGlhbCBpbnRlZ3JhdGlvbiB3aXRoIGV4aXN0aW5nIGVtYWlsIGZpbHRlcmluZyBzb2x1dGlvbnMNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKkludHJvZHVjdGlvbioqDQoNCiMjIyMgKipCYWNrZ3JvdW5kIGFuZCBNb3RpdmF0aW9uKioNCg0KU3BhbSBlbWFpbHMgY29udGludWUgdG8gYmUgYSBzaWduaWZpY2FudCBjaGFsbGVuZ2UgZm9yIG9yZ2FuaXphdGlvbnMsIGxlYWRpbmcgdG8gc2VjdXJpdHkgcmlza3MsIGRlY3JlYXNlZCBwcm9kdWN0aXZpdHksIGFuZCB0aGUgcG90ZW50aWFsIGxvc3Mgb2YgbGVnaXRpbWF0ZSBjb21tdW5pY2F0aW9ucy4NCkFjY29yZGluZyB0byByZWNlbnQgc3R1ZGllcywgc3BhbSBhY2NvdW50cyBmb3IgKipvdmVyIDUwJSoqIG9mIGFsbCBlbWFpbCB0cmFmZmljIHdvcmxkd2lkZSwgbWFraW5nIGVmZmljaWVudCBmaWx0ZXJpbmcgdGVjaG5pcXVlcyBhIG5lY2Vzc2l0eS4NCk9yZ2FuaXphdGlvbnMgbXVzdCBpbXBsZW1lbnQgcm9idXN0IHNwYW0gY2xhc3NpZmljYXRpb24gbW9kZWxzIHRoYXQgY2FuIGJhbGFuY2UgdGhlIHJpc2tzIG9mICoqZmFsc2UgcG9zaXRpdmVzKiogKGxlZ2l0aW1hdGUgZW1haWxzIGluY29ycmVjdGx5IGNsYXNzaWZpZWQgYXMgc3BhbSkgYW5kICoqZmFsc2UgbmVnYXRpdmVzKiogKHNwYW0gZW1haWxzIGJ5cGFzc2luZyBmaWx0ZXJzKS4NCg0KVGhpcyBjYXNlIHN0dWR5IGZvY3VzZXMgb24gYnVpbGRpbmcgYSAqKnNwYW0gY2xhc3NpZmljYXRpb24gbW9kZWwgdXNpbmcgTmHDr3ZlIEJheWVzIGFuZCBjbHVzdGVyaW5nIHRlY2huaXF1ZXMqKiwgZXhwYW5kaW5nIHRvIGluY2x1ZGUgKipSYW5kb20gRm9yZXN0LCBYR0Jvb3N0LCBhbmQgc2VtaS1zdXBlcnZpc2VkIGxlYXJuaW5nKiogZm9yIGltcHJvdmVkIHBlcmZvcm1hbmNlLg0KT3VyIG9iamVjdGl2ZSBpcyB0byBkZXZlbG9wIGEgc2NhbGFibGUgYW5kIGdlbmVyYWxpemFibGUgc3BhbSBmaWx0ZXIgdGhhdCBtaW5pbWl6ZXMgYm90aCBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcyB3aGlsZSBlbnN1cmluZyBhY2N1cmF0ZSBjbGFzc2lmaWNhdGlvbi4NCg0KIyMjIyAqKk9yZ2FuaXphdGlvbmFsIENvbnRleHQqKg0KDQpUaGlzIHN0dWR5IGlzIG1vdGl2YXRlZCBieSBhIHJlcXVlc3QgZnJvbSB0aGUgKipJVCBkZXBhcnRtZW50KiosIHdoaWNoIGZhY2VzIGFuIG92ZXJ3aGVsbWluZyBudW1iZXIgb2Ygc3BhbSBlbWFpbHMuDQpUaGUga2V5IGNvbmNlcm5zIGluY2x1ZGU6DQoNCjFcLg0KKipPdmVyLWZpbHRlcmluZzoqKiBDcml0aWNhbCBidXNpbmVzcyBlbWFpbHMgbWF5IGJlIG1pc3Rha2VubHkgY2F0ZWdvcml6ZWQgYXMgc3BhbS4NCg0KMlwuDQoqKlVuZGVyLWZpbHRlcmluZzoqKiBUb28gbXVjaCBzcGFtIG1heSBjbHV0dGVyIGluYm94ZXMsIHJlZHVjaW5nIHByb2R1Y3Rpdml0eS4NCg0KM1wuDQoqKlNjYWxhYmlsaXR5OioqIFRoZSBzb2x1dGlvbiBtdXN0IGhhbmRsZSBsYXJnZSB2b2x1bWVzIG9mIGVtYWlsIGRhdGEgZWZmaWNpZW50bHkuDQoNCiMjIyMgKipPYmplY3RpdmVzIGFuZCBSZXNlYXJjaCBRdWVzdGlvbnMqKg0KDQpUaGUga2V5IG9iamVjdGl2ZXMgb2YgdGhpcyBzdHVkeSBhcmU6DQoNCjFcLg0KKipEZXZlbG9wIGEgcm9idXN0IHNwYW0gY2xhc3NpZmllcioqIGNhcGFibGUgb2YgZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBzcGFtIGFuZCBoYW0gKGxlZ2l0aW1hdGUpIGVtYWlscy4NCg0KMlwuDQoqKkluY29ycG9yYXRlIGNsdXN0ZXJpbmcqKiB0byBkZXRlY3QgdW5kZXJseWluZyBzcGFtIHBhdHRlcm5zLg0KDQozXC4NCioqRXZhbHVhdGUgbXVsdGlwbGUgY2xhc3NpZmljYXRpb24gbW9kZWxzKiosIGluY2x1ZGluZyBOYcOvdmUgQmF5ZXMsIFJhbmRvbSBGb3Jlc3QsIGFuZCBYR0Jvb3N0Lg0KDQo0XC4NCioqSW1wbGVtZW50IHNlbWktc3VwZXJ2aXNlZCBsZWFybmluZyoqIHRvIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2Ugd2l0aCB1bmxhYmVsZWQgZGF0YS4NCg0KNVwuDQoqKkFzc2VzcyBtb2RlbCBwZXJmb3JtYW5jZSB1c2luZyBjcm9zcy12YWxpZGF0aW9uKiosIGNvbmZ1c2lvbiBtYXRyaWNlcywgYW5kIEFVQy1ST0MgY3VydmVzLg0KDQojIyMjICoqT3V0bGluZSBvZiB0aGUgV2hpdGUgUGFwZXIqKg0KDQpUaGUgcmVtYWluZGVyIG9mIHRoaXMgd2hpdGUgcGFwZXIgaXMgc3RydWN0dXJlZCBhcyBmb2xsb3dzOiAtICoqTGl0ZXJhdHVyZSBSZXZpZXc6KiogT3ZlcnZpZXcgb2YgZXhpc3Rpbmcgc3BhbSBmaWx0ZXJpbmcgbWV0aG9kcy4NCi0gKipEYXRhIERlc2NyaXB0aW9uICYgUHJlcGFyYXRpb246KiogSG93IHRoZSBkYXRhc2V0IHdhcyBjcmVhdGVkLCBjbGVhbmVkLCBhbmQgcHJvY2Vzc2VkLg0KLSAqKk1ldGhvZG9sb2d5ICYgTW9kZWwgQnVpbGRpbmc6KiogRXhwbGFuYXRpb24gb2YgTmHDr3ZlIEJheWVzLCBjbHVzdGVyaW5nIHRlY2huaXF1ZXMsIGFuZCBzdXBlcnZpc2VkIGxlYXJuaW5nIG1vZGVscy4NCi0gKipSZXN1bHRzICYgRXZhbHVhdGlvbjoqKiBQZXJmb3JtYW5jZSBtZXRyaWNzLCBjb25mdXNpb24gbWF0cmljZXMsIGFuZCBST0MgY3VydmVzLg0KLSAqKkRpc2N1c3Npb24gJiBJbnNpZ2h0czoqKiBBbmFseXNpcyBvZiBmaW5kaW5ncywgbGltaXRhdGlvbnMsIGFuZCBwb3RlbnRpYWwgaW1wcm92ZW1lbnRzLg0KLSAqKkNvbmNsdXNpb24gJiBSZWNvbW1lbmRhdGlvbnM6KiogU3VtbWFyeSBvZiByZXN1bHRzIGFuZCBmdXR1cmUgd29yay4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKk1ldGhvZG9sb2d5IGFuZCBNb2RlbCBCdWlsZGluZyoqDQoNCiMjIyMgKipGZWF0dXJlIEVuZ2luZWVyaW5nIGFuZCBEYXRhIFByZXBhcmF0aW9uKioNCg0KLSAgICoqRW1haWwgVGV4dCBQcmVwcm9jZXNzaW5nOioqDQogICAgLSAgIFRva2VuaXphdGlvbiwgc3RvcHdvcmQgcmVtb3ZhbCwgc3RlbW1pbmcvbGVtbWF0aXphdGlvbg0KICAgIC0gICBGZWF0dXJlIGV4dHJhY3Rpb24gdXNpbmcgVEYtSURGIGFuZCBCb1cNCi0gICAqKkNsdXN0ZXJpbmcgQW5hbHlzaXM6KioNCiAgICAtICAgSy1NZWFucyBhbmQgREJTQ0FOIHVzZWQgdG8gZXhwbG9yZSBwYXR0ZXJucyBpbiBzcGFtIGVtYWlscw0KICAgIC0gICBEQlNDQU4gc2hvd2VkIGJldHRlciBzZXBhcmF0aW9uIG9mIGNsdXN0ZXJzDQotICAgKipTdXBlcnZpc2VkIExlYXJuaW5nIE1vZGVsczoqKg0KICAgIC0gICBOYcOvdmUgQmF5ZXMsIFJhbmRvbSBGb3Jlc3QsIFhHQm9vc3QsIGFuZCBTZWxmLVRyYWluaW5nIFhHQm9vc3QNCi0gICAqKkh5cGVycGFyYW1ldGVyIFR1bmluZzoqKg0KICAgIC0gICBDcm9zcy12YWxpZGF0aW9uIGFuZCBncmlkIHNlYXJjaCBmb3IgbW9kZWwgb3B0aW1pemF0aW9uDQoNCiMjIyMgKipSZXN1bHRzIGFuZCBFdmFsdWF0aW9uKioNCg0KLSAgICoqTmHDr3ZlIEJheWVzIGFjaGlldmVkIDk4LjQlIGFjY3VyYWN5KioNCi0gICAqKlhHQm9vc3Qgb3V0cGVyZm9ybWVkIE5hw692ZSBCYXllcyB3aXRoIDk5JSBhY2N1cmFjeSoqDQotICAgKipTZWxmLVRyYWluaW5nIFhHQm9vc3QgcmVhY2hlZCAxMDAlIGFjY3VyYWN5IHdpdGggMCBmYWxzZSBuZWdhdGl2ZXMqKg0KLSAgICoqQ2x1c3RlcmluZyBwcm92aWRlZCBpbnNpZ2h0cyBidXQgd2FzIG5vdCB1c2VkIGZvciBmaW5hbCBjbGFzc2lmaWNhdGlvbioqDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipDb25jbHVzaW9uIGFuZCBSZWNvbW1lbmRhdGlvbnMqKg0KDQotICAgKipTZWxmLVRyYWluaW5nIFhHQm9vc3QgaXMgdGhlIGJlc3QgbW9kZWwqKiB3aXRoIDEwMCUgYWNjdXJhY3kuDQotICAgKipYR0Jvb3N0IGlzIGEgc3Ryb25nIGFsdGVybmF0aXZlKiogZm9yIGRlcGxveW1lbnQgaWYgc2VtaS1zdXBlcnZpc2VkIGxlYXJuaW5nIGlzIG5vdCBmZWFzaWJsZS4NCi0gICAqKlJlZ3VsYXIgcmV0cmFpbmluZyBpcyBuZWNlc3NhcnkqKiBhcyBzcGFtIHBhdHRlcm5zIGV2b2x2ZS4NCi0gICAqKkZ1dHVyZSB3b3JrOioqIEV4cGxvcmluZyBkZWVwIGxlYXJuaW5nIG1vZGVscyBsaWtlIHRyYW5zZm9ybWVycyBmb3IgaW1wcm92ZWQgc3BhbSBkZXRlY3Rpb24uDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqTGl0ZXJhdHVyZSBSZXZpZXcgLyBSZWxhdGVkIFdvcmsqKg0KDQojIyMjICoqRXhpc3RpbmcgU3BhbSBEZXRlY3Rpb24gVGVjaG5pcXVlcyoqDQoNClNwYW0gZmlsdGVyaW5nIGhhcyBiZWVuIGEgY3JpdGljYWwgYXJlYSBvZiByZXNlYXJjaCBpbiAqKm1hY2hpbmUgbGVhcm5pbmcqKiBhbmQgKipuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcgKE5MUCkqKiBmb3IgZGVjYWRlcy4NClZhcmlvdXMgbWV0aG9kcyBoYXZlIGJlZW4gZGV2ZWxvcGVkIHRvIGNsYXNzaWZ5IGVtYWlscyBhcyBzcGFtIG9yIGhhbSwgYnJvYWRseSBjYXRlZ29yaXplZCBpbnRvICoqcnVsZS1iYXNlZCBmaWx0ZXJpbmcsIHN1cGVydmlzZWQgbGVhcm5pbmcsIGFuZCB1bnN1cGVydmlzZWQgbGVhcm5pbmcqKiBhcHByb2FjaGVzLg0KDQoxLiAgKipSdWxlLUJhc2VkIEZpbHRlcmluZyAoSGV1cmlzdGljIEFwcHJvYWNoZXMpKioNCiAgICAtICAgRWFybHkgc3BhbSBmaWx0ZXJzLCBzdWNoIGFzICoqU3BhbUFzc2Fzc2luKiosIHJlbGllZCBvbiAqKm1hbnVhbGx5IGNyYWZ0ZWQgcnVsZXMqKiB0byBpZGVudGlmeSBzcGFtLiBUaGVzZSBydWxlcyBldmFsdWF0ZWQgKip3b3JkIGZyZXF1ZW5jeSwgYmxhY2tsaXN0cywgYW5kIG1ldGFkYXRhKiouDQogICAgLSAgIEFsdGhvdWdoIGVmZmVjdGl2ZSBmb3IgcHJlZGVmaW5lZCBzcGFtIHBhdHRlcm5zLCBydWxlLWJhc2VkIHN5c3RlbXMgc3RydWdnbGVkIHdpdGggKiphZGFwdGFiaWxpdHkqKiB0byBuZXcgc3BhbSB0cmVuZHMuDQoyLiAgKipTdXBlcnZpc2VkIExlYXJuaW5nLUJhc2VkIEFwcHJvYWNoZXMqKg0KICAgIC0gICAqKk5hw692ZSBCYXllcyAoTkIpKio6IEEgKipwcm9iYWJpbGlzdGljIGNsYXNzaWZpZXIqKiB0aGF0IGFzc3VtZXMgZmVhdHVyZSBpbmRlcGVuZGVuY2UgYW5kIGNhbGN1bGF0ZXMgdGhlIGxpa2VsaWhvb2Qgb2YgYW4gZW1haWwgYmVpbmcgc3BhbSBiYXNlZCBvbiB3b3JkIHByb2JhYmlsaXRpZXMuDQogICAgLSAgICoqU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMgKFNWTXMpKio6IEEgc3Ryb25nIGNsYXNzaWZpZXIgZm9yIGhpZ2gtZGltZW5zaW9uYWwgdGV4dCBkYXRhLCBvZnRlbiBvdXRwZXJmb3JtaW5nIE5hw692ZSBCYXllcyBpbiBwcmVjaXNpb24gYW5kIHJlY2FsbC4NCiAgICAtICAgKipSYW5kb20gRm9yZXN0ICYgWEdCb29zdCoqOiBFbnNlbWJsZSBtZXRob2RzIHRoYXQgYWdncmVnYXRlIG11bHRpcGxlIGRlY2lzaW9uIHRyZWVzIGZvciBpbXByb3ZlZCBhY2N1cmFjeSBhbmQgZmVhdHVyZSBpbXBvcnRhbmNlIGV2YWx1YXRpb24uDQogICAgLSAgICoqRGVlcCBMZWFybmluZyBBcHByb2FjaGVzKio6IE1vZGVscyBsaWtlICoqUmVjdXJyZW50IE5ldXJhbCBOZXR3b3JrcyAoUk5OcykqKiBhbmQgKipUcmFuc2Zvcm1lcnMgKGUuZy4sIEJFUlQpKiogaGF2ZSByZWNlbnRseSBzaG93biBzdGF0ZS1vZi10aGUtYXJ0IHBlcmZvcm1hbmNlIGluIHNwYW0gY2xhc3NpZmljYXRpb24uDQozLiAgKipVbnN1cGVydmlzZWQgTGVhcm5pbmcgZm9yIFNwYW0gRGV0ZWN0aW9uKioNCiAgICAtICAgKipDbHVzdGVyaW5nIChLLU1lYW5zLCBEQlNDQU4sIEhpZXJhcmNoaWNhbCkqKjogR3JvdXBzIHNpbWlsYXIgZW1haWxzIHRvZ2V0aGVyIHdpdGhvdXQgcHJlZGVmaW5lZCBsYWJlbHMuIFRoaXMgaXMgdXNlZnVsIGZvciBkaXNjb3ZlcmluZyAqKnByZXZpb3VzbHkgdW5zZWVuIHNwYW0gcGF0dGVybnMqKi4NCiAgICAtICAgKipTZW1pLVN1cGVydmlzZWQgTGVhcm5pbmcqKjogVGVjaG5pcXVlcyBsaWtlICoqU2VsZi1UcmFpbmluZyBhbmQgTGFiZWwgUHJvcGFnYXRpb24qKiBlbmFibGUgdHJhaW5pbmcgbW9kZWxzIHdpdGggYSBtaXggb2YgbGFiZWxlZCBhbmQgdW5sYWJlbGVkIGRhdGEgdG8gaW1wcm92ZSBnZW5lcmFsaXphdGlvbi4NCg0KIyMjIyAqKk5hw692ZSBCYXllcyBmb3IgU3BhbSBEZXRlY3Rpb24qKg0KDQpOYcOvdmUgQmF5ZXMgaXMgYSBwb3B1bGFyIHNwYW0gZGV0ZWN0aW9uIG1vZGVsIGR1ZSB0byBpdHM6IDEuDQoqKkZhc3QgVHJhaW5pbmcgJiBJbmZlcmVuY2UqKiDigJMgV29ya3MgZWZmaWNpZW50bHkgZXZlbiBvbiBsYXJnZSBkYXRhc2V0cy4NCjIuDQoqKlJvYnVzdG5lc3MgdG8gSGlnaC1EaW1lbnNpb25hbCBEYXRhKiog4oCTIEhhbmRsZXMgdGV4dCBkYXRhIHdlbGwgdXNpbmcgKipURi1JREYqKiBvciAqKkJhZy1vZi1Xb3JkcyAoQm9XKSoqIHJlcHJlc2VudGF0aW9ucy4NCjMuDQoqKlN0cm9uZyBUaGVvcmV0aWNhbCBGb3VuZGF0aW9uKiog4oCTIEJhc2VkIG9uICoqQmF5ZXPigJkgVGhlb3JlbSoqLCBhc3N1bWluZyBmZWF0dXJlIGluZGVwZW5kZW5jZToNCg0KJCQNClAoU3BhbSB8IEVtYWlsKSA9IFxmcmFje1AoRW1haWwgfCBTcGFtKSBcY2RvdCBQKFNwYW0pfXtQKEVtYWlsKX0NCiQkDQoNCkRlc3BpdGUgaXRzIGFkdmFudGFnZXMsIE5hw692ZSBCYXllcyBjYW4gc3RydWdnbGUgd2l0aDogLSAqKldvcmQgZGVwZW5kZW5jaWVzKiogKGFzc3VtcHRpb24gb2YgZmVhdHVyZSBpbmRlcGVuZGVuY2UgaXMgb2Z0ZW4gdW5yZWFsaXN0aWMpLg0KLSAqKkhhbmRsaW5nIG9mIHJhcmUgd29yZHMqKiAobWl0aWdhdGVkIHVzaW5nICoqTGFwbGFjZSBzbW9vdGhpbmcqKikuDQoNCiMjIyMgKipDbHVzdGVyaW5nIGluIFNwYW0gRGV0ZWN0aW9uKioNCg0KQ2x1c3RlcmluZyBtZXRob2RzIGxpa2UgKipLLU1lYW5zKiogYW5kICoqREJTQ0FOKiogY2FuOiAxLg0KKipEZXRlY3QgaGlkZGVuIHNwYW0gcGF0dGVybnMqKiBieSBncm91cGluZyBzaW1pbGFyIGVtYWlscy4NCjIuDQoqKlByb3ZpZGUgYWRkaXRpb25hbCBmZWF0dXJlcyoqIGZvciBzdXBlcnZpc2VkIGxlYXJuaW5nIG1vZGVscy4NCjMuDQoqKkFpZCBpbiBzZW1pLXN1cGVydmlzZWQgbGVhcm5pbmcqKiBieSBwc2V1ZG8tbGFiZWxpbmcgdW5sYWJlbGVkIGRhdGEuDQoNCkNoYWxsZW5nZXMgaW5jbHVkZTogLSAqKkNob29zaW5nIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyAoayBmb3IgSy1NZWFucykqKiAtICoqSGFuZGxpbmcgaGlnaC1kaW1lbnNpb25hbCB0ZXh0IGRhdGEqKiAoUENBIG9yIHQtU05FIGNhbiBoZWxwIHZpc3VhbGl6ZSBjbHVzdGVycykuDQoNCiMjIyMgKipJbnRlZ3JhdGlvbiBvZiBDbHVzdGVyaW5nICYgTmHDr3ZlIEJheWVzKioNCg0KU2V2ZXJhbCBzdHVkaWVzIHN1Z2dlc3QgY29tYmluaW5nIGNsdXN0ZXJpbmcgd2l0aCBOYcOvdmUgQmF5ZXM6IC0gQ2x1c3RlcmluZyBjYW4gKipwcmUtbGFiZWwgZGF0YSoqIGZvciBOYcOvdmUgQmF5ZXMgdHJhaW5pbmcuDQotIE5hw692ZSBCYXllcyBjYW4gYmUgKiplbmhhbmNlZCB3aXRoIGNsdXN0ZXItYmFzZWQgZmVhdHVyZXMqKi4NCi0gU2VtaS1zdXBlcnZpc2VkIGxlYXJuaW5nIHZpYSAqKkxhYmVsIFByb3BhZ2F0aW9uKiogY2FuIGltcHJvdmUgYWNjdXJhY3kgd2hlbiBsYWJlbGVkIGRhdGEgaXMgbGltaXRlZC4NCg0KIyMjIyAqKkNvbmNsdXNpb24gZnJvbSBSZWxhdGVkIFdvcmsqKg0KDQotICAgKipOYcOvdmUgQmF5ZXMgcmVtYWlucyBhIHN0cm9uZyBiYXNlbGluZSoqIGZvciBzcGFtIGRldGVjdGlvbi4NCi0gICAqKkNsdXN0ZXJpbmcgY2FuIHByb3ZpZGUgaW5zaWdodHMgYW5kIGZlYXR1cmVzKiogZm9yIGJldHRlciBjbGFzc2lmaWNhdGlvbi4NCi0gICAqKkFkdmFuY2VkIG1vZGVscyAoWEdCb29zdCwgU2VsZi1UcmFpbmluZywgTGFiZWwgUHJvcGFnYXRpb24pKiogaW1wcm92ZSBzcGFtIGRldGVjdGlvbiBieSBsZXZlcmFnaW5nICoqZW5zZW1ibGUgbGVhcm5pbmcgYW5kIHNlbWktc3VwZXJ2aXNlZCBtZXRob2RzKiouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipEYXRhIERlc2NyaXB0aW9uIGFuZCBQcmVwYXJhdGlvbioqDQoNCiMjIyMgKipBLiBTb3VyY2VzIG9mIERhdGEqKg0KDQpUaGUgZGF0YXNldCB1c2VkIGluIHRoaXMgc3R1ZHkgd2FzIGV4dHJhY3RlZCBmcm9tICoqU3BhbUFzc2Fzc2luKiosIGEgd2VsbC1rbm93biBzcGFtIGZpbHRlcmluZyBmcmFtZXdvcmsuDQpUaGUgcmF3IGRhdGEgY29uc2lzdGVkIG9mICoqcGxhaW4tdGV4dCBlbWFpbCBtZXNzYWdlcyoqLCBjYXRlZ29yaXplZCBpbnRvOiAtICoqSGFtIChsZWdpdGltYXRlIGVtYWlscykqKjogRW1haWxzIHRoYXQgc2hvdWxkIHJlYWNoIHRoZSBpbmJveC4NCi0gKipTcGFtICh1bndhbnRlZCBlbWFpbHMpKio6IFVuc29saWNpdGVkIGVtYWlscywgb2Z0ZW4gY29udGFpbmluZyBhZHZlcnRpc2VtZW50cywgc2NhbXMsIG9yIHBoaXNoaW5nIGF0dGVtcHRzLg0KDQpTaW5jZSB0aGUgZGF0YXNldCB3YXMgcHJvdmlkZWQgYXMgKipyYXcgZW1haWwgZmlsZXMqKiwgbm8gc3RydWN0dXJlZCBDU1Ygb3IgdGFidWxhciBmb3JtYXQgd2FzIGF2YWlsYWJsZS4NClRoZSBkYXRhc2V0IGhhZCB0byBiZSAqKm1hbnVhbGx5IHBhcnNlZCoqLCBhbmQgcmVsZXZhbnQgY29udGVudCBoYWQgdG8gYmUgZXh0cmFjdGVkLg0KDQojIyMjICoqQi4gRGF0YSBWb2x1bWUgYW5kIFN0cnVjdHVyZSoqDQoNClRoZSBkYXRhc2V0IGNvbnRhaW5lZCAqKnNldmVyYWwgdGhvdXNhbmQqKiBlbWFpbHMsIHdpdGggdGhlIGZvbGxvd2luZyBkaXN0cmlidXRpb246IC0gKipIYW0gKG5vbi1zcGFtIGVtYWlscykqKjogQXBwcm94aW1hdGVseSAqKjcsNTAwKiogZXhhbXBsZXMuDQotICoqU3BhbSBlbWFpbHMqKjogQXBwcm94aW1hdGVseSAqKjMsNTAwKiogZXhhbXBsZXMuDQotICoqVG90YWwgZGF0YXNldCBzaXplKio6IFx+KioxMCwwMDAgZW1haWxzKiouDQoNCktleSBzdHJ1Y3R1cmFsIGFzcGVjdHMgb2YgdGhlIGRhdGFzZXQ6IDEuDQoqKlJhdyB0ZXh0IGZvcm1hdCAod2l0aCBoZWFkZXJzLCBib2R5LCBhdHRhY2htZW50cywgZXRjLikuKiogMi4NCioqTWV0YWRhdGEgaW5jbHVkZWQgc2VuZGVyL3JlY2VpdmVyIGZpZWxkcyBhbmQgdGltZXN0YW1wcy4qKiAzLg0KKipUZXh0IGRhdGEgY29udGFpbmVkIFVSTHMsIEhUTUwgZm9ybWF0dGluZywgYW5kIGVuY29kZWQgY2hhcmFjdGVycy4qKg0KDQojIyMjICoqQy4gUHJlLVByb2Nlc3NpbmcgUGlwZWxpbmUqKg0KDQpQcmVwcm9jZXNzaW5nIHdhcyBlc3NlbnRpYWwgdG8gY29udmVydCByYXcgZW1haWxzIGludG8gYSBzdHJ1Y3R1cmVkIGZvcm1hdCBmb3IgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuDQpUaGUgZm9sbG93aW5nIHN0ZXBzIHdlcmUgcGVyZm9ybWVkOg0KDQojIyMjIyAqKjEuIEVtYWlsIFRleHQgRXh0cmFjdGlvbioqDQoNCi0gICBSZW1vdmVkICoqZW1haWwgaGVhZGVycyoqIChlLmcuLCBgRnJvbWAsIGBUb2AsIGBTdWJqZWN0YCkuDQotICAgRXh0cmFjdGVkICoqYm9keSBjb250ZW50Kiogd2hpbGUgaWdub3JpbmcgbWV0YWRhdGEuDQotICAgKipSZW1vdmVkIEhUTUwgdGFncyoqIHRvIGNsZWFuIGZvcm1hdHRpbmcgaW5jb25zaXN0ZW5jaWVzLg0KDQojIyMjIyAqKjIuIFRva2VuaXphdGlvbiAmIENsZWFuaW5nKioNCg0KLSAgICoqVG9rZW5pemVkIHRleHQqKjogQ29udmVydGVkIGVtYWlsIGNvbnRlbnQgaW50byBpbmRpdmlkdWFsIHdvcmRzLg0KLSAgICoqTG93ZXJjYXNlZCBhbGwgdGV4dCoqIHRvIGF2b2lkIGNhc2Utc2Vuc2l0aXZlIGRpc2NyZXBhbmNpZXMuDQotICAgKipSZW1vdmVkIHB1bmN0dWF0aW9uLCBudW1iZXJzLCBhbmQgc3BlY2lhbCBjaGFyYWN0ZXJzKiouDQotICAgKipGaWx0ZXJlZCBvdXQgZW1haWwgYWRkcmVzc2VzLCBVUkxzLCBhbmQgY29tbW9uIHN5bWJvbHMqKi4NCg0KIyMjIyMgKiozLiBTdG9wd29yZCBSZW1vdmFsICYgU3RlbW1pbmcqKg0KDQotICAgKipTdG9wd29yZHMqKiAoZS5nLiwg4oCcdGhl4oCdLCDigJxhbmTigJ0sIOKAnGlz4oCdKSB3ZXJlIHJlbW92ZWQgdG8gZm9jdXMgb24gbWVhbmluZ2Z1bCB3b3Jkcy4NCi0gICAqKlBvcnRlciBTdGVtbWluZyBBbGdvcml0aG0qKiB3YXMgYXBwbGllZCB0byByZWR1Y2Ugd29yZHMgdG8gdGhlaXIgcm9vdCBmb3JtIChlLmcuLCDigJxydW5uaW5n4oCdIOKGkiDigJxydW7igJ0pLg0KDQojIyMjIyAqKjQuIEZlYXR1cmUgRW5naW5lZXJpbmcqKg0KDQpUd28gcHJpbWFyeSBtZXRob2RzIHdlcmUgdXNlZCB0byBjb252ZXJ0IHRleHQgaW50byBudW1lcmljYWwgZmVhdHVyZXM6IC0gKipCYWctb2YtV29yZHMgKEJvVyk6KiogQ291bnRzIHRoZSBvY2N1cnJlbmNlIG9mIHdvcmRzIGluIGVhY2ggZW1haWwuDQotICoqVEYtSURGIChUZXJtIEZyZXF1ZW5jeS1JbnZlcnNlIERvY3VtZW50IEZyZXF1ZW5jeSk6KiogTWVhc3VyZXMgd29yZCBpbXBvcnRhbmNlIGFjcm9zcyBhbGwgZW1haWxzLg0KDQpBIHZvY2FidWxhcnkgc2l6ZSBvZiAqKjEwLDAwMCB3b3JkcyoqIHdhcyBjaG9zZW4sIHJlbW92aW5nIGluZnJlcXVlbnQgdGVybXMuDQoNCiMjIyMjICoqNS4gSGFuZGxpbmcgSW1iYWxhbmNlZCBEYXRhKioNCg0KU2luY2Ugc3BhbSBlbWFpbHMgd2VyZSB1bmRlcnJlcHJlc2VudGVkLCAqKmRhdGEgYXVnbWVudGF0aW9uKiogd2FzIHBlcmZvcm1lZDogLSAqKlJhbmRvbSBPdmVyc2FtcGxpbmc6KiogRHVwbGljYXRlZCBzb21lIHNwYW0gbWVzc2FnZXMgdG8gYmFsYW5jZSBjbGFzc2VzLg0KLSAqKlN5bnRoZXRpYyBEYXRhIEdlbmVyYXRpb246KiogQ3JlYXRlZCAqKmFydGlmaWNpYWwgc3BhbSBlbWFpbHMqKiB3aXRoIGNvbW1vbiBzcGFtIHBocmFzZXMuDQoNCiMjIyMjICoqNi4gVHJhaW4tVGVzdCBTcGxpdCAmIENyb3NzLVZhbGlkYXRpb24qKg0KDQpUbyBlbnN1cmUgZ2VuZXJhbGl6YXRpb246IC0gKio4MCUgb2YgdGhlIGRhdGEgd2FzIHVzZWQgZm9yIHRyYWluaW5nLCAyMCUgZm9yIHRlc3RpbmcuKiogLSAqKlN0cmF0aWZpZWQgMTAtRm9sZCBDcm9zcy1WYWxpZGF0aW9uKiogd2FzIHBlcmZvcm1lZCB0byAqKmV2YWx1YXRlIHBlcmZvcm1hbmNlIGFjcm9zcyBtdWx0aXBsZSBzcGxpdHMqKi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN1bW1hcnkgb2YgRGF0YSBQcmVwYXJhdGlvbiBTdGVwcyoqDQoNCnwgU3RlcCB8IERlc2NyaXB0aW9uIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICoqVGV4dCBFeHRyYWN0aW9uKiogfCBSZW1vdmVkIGhlYWRlcnMsIG1ldGFkYXRhLCBhbmQgbm9uLXRleHQgZWxlbWVudHMgfA0KfCAqKkNsZWFuaW5nKiogfCBMb3dlcmNhc2VkIHRleHQsIHJlbW92ZWQgcHVuY3R1YXRpb24sIG51bWJlcnMsIGFuZCBVUkxzIHwNCnwgKipUb2tlbml6YXRpb24qKiB8IFNwbGl0IGVtYWlscyBpbnRvIHdvcmRzIGZvciBmdXJ0aGVyIHByb2Nlc3NpbmcgfA0KfCAqKlN0b3B3b3JkIFJlbW92YWwgJiBTdGVtbWluZyoqIHwgS2VwdCBvbmx5IG1lYW5pbmdmdWwgd29yZHMsIGFwcGxpZWQgc3RlbW1pbmcgfA0KfCAqKkZlYXR1cmUgRW5naW5lZXJpbmcqKiB8IENvbnZlcnRlZCB0ZXh0IHRvIG51bWVyaWNhbCB2ZWN0b3JzIHVzaW5nIFRGLUlERiAmIEJvVyB8DQp8ICoqSGFuZGxpbmcgSW1iYWxhbmNlKiogfCBVc2VkIG92ZXJzYW1wbGluZyBhbmQgc3ludGhldGljIHNwYW0gZ2VuZXJhdGlvbiB8DQp8ICoqVHJhaW4tVGVzdCBTcGxpdCoqIHwgODAlIHRyYWluaW5nLCAyMCUgdGVzdGluZyB8DQp8ICoqQ3Jvc3MtVmFsaWRhdGlvbioqIHwgQXBwbGllZCAqKjEwLWZvbGQgc3RyYXRpZmllZCBjcm9zcy12YWxpZGF0aW9uKiogfA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqTWV0aG9kb2xvZ3kgYW5kIE1vZGVsIEJ1aWxkaW5nKioNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBvdXRsaW5lIHRoZSBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoZXMgdXNlZCB0byBjbGFzc2lmeSBlbWFpbHMgYXMgc3BhbSBvciBub24tc3BhbS4NClRoZSBtZXRob2RvbG9neSBjb25zaXN0cyBvZiAqKnR3byBtYWluIGNvbXBvbmVudHMqKjpcDQoxLg0KKipDbHVzdGVyaW5nIChVbnN1cGVydmlzZWQgTGVhcm5pbmcpKio6IFVzZWQgYXMgYW4gZXhwbG9yYXRvcnkgc3RlcCB0byBpZGVudGlmeSBoaWRkZW4gc3RydWN0dXJlcyBpbiB0aGUgZGF0YXNldC5cDQoyLg0KKipOYcOvdmUgQmF5ZXMgQ2xhc3NpZmllciAoU3VwZXJ2aXNlZCBMZWFybmluZykqKjogVGhlIHByaW1hcnkgbW9kZWwgZm9yIGNsYXNzaWZpY2F0aW9uLCBvcHRpbWl6ZWQgd2l0aCBoeXBlcnBhcmFtZXRlciB0dW5pbmcuDQoNCkFkZGl0aW9uYWxseSwgKipjcm9zcy12YWxpZGF0aW9uKiogd2FzIGFwcGxpZWQgdG8gZW5zdXJlIHRoZSByb2J1c3RuZXNzIG9mIG91ciByZXN1bHRzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkEuIENsdXN0ZXJpbmcgQ29tcG9uZW50KioNCg0KIyMjICoqMS4gQ2hvaWNlIG9mIEFsZ29yaXRobSoqDQoNClRvIGV4cGxvcmUgKip1bnN1cGVydmlzZWQgcGF0dGVybnMqKiwgd2UgZXhwZXJpbWVudGVkIHdpdGggKip0d28gY2x1c3RlcmluZyB0ZWNobmlxdWVzKio6IC0gKipLLU1lYW5zIENsdXN0ZXJpbmcqKiAoUGFydGl0aW9uLWJhc2VkKSAtICoqREJTQ0FOKiogKERlbnNpdHktYmFzZWQpDQoNCkFmdGVyIGV2YWx1YXRpbmcgbXVsdGlwbGUgY29uZmlndXJhdGlvbnMsICoqSy1NZWFucyB3aXRoIGs9OSBjbHVzdGVycyoqIHByb3ZpZGVkIHRoZSBiZXN0IHNlcGFyYXRpb24uDQoNCiMjIyAqKjIuIE1vdGl2YXRpb25zIGZvciBDbHVzdGVyaW5nKioNCg0KLSAgIENsdXN0ZXJpbmcgY2FuIGhlbHAgaWRlbnRpZnkgZGlzdGluY3QgKipzcGFtIGNhdGVnb3JpZXMqKiAoZS5nLiwgcGhpc2hpbmcsIHByb21vdGlvbmFsIHNwYW0pLg0KLSAgIENsdXN0ZXIgbWVtYmVyc2hpcHMgY2FuIHNlcnZlIGFzICoqYWRkaXRpb25hbCBmZWF0dXJlcyoqIGluIHN1cGVydmlzZWQgbW9kZWxzLg0KLSAgIFVuc3VwZXJ2aXNlZCBsZWFybmluZyBoZWxwcyBhbmFseXplICoqbWlzY2xhc3NpZmllZCBlbWFpbHMqKiAoZS5nLiwgYm9yZGVybGluZSBjYXNlcykuDQoNCiMjIyAqKjMuIEltcGxlbWVudGF0aW9uIERldGFpbHMqKg0KDQoxLiAgKipURi1JREYgZmVhdHVyZSB2ZWN0b3JzKiogd2VyZSB1c2VkIGFzIGlucHV0IHRvIHRoZSBjbHVzdGVyaW5nIG1vZGVscy4NCjIuICAqKkRpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiAoUENBLCB0LVNORSkqKiB3YXMgYXBwbGllZCBmb3IgdmlzdWFsaXphdGlvbi4NCjMuICAqKkNsdXN0ZXIgYXNzaWdubWVudHMqKiB3ZXJlIHVzZWQgYXMgYW4gYWRkaXRpb25hbCBpbnB1dCBmZWF0dXJlIGZvciBOYcOvdmUgQmF5ZXMuDQoNCiMjIyMgKipDbHVzdGVyaW5nIFJlc3VsdHMqKg0KDQp8IENsdXN0ZXJpbmcgQWxnb3JpdGhtIHwgU2lsaG91ZXR0ZSBTY29yZSB8IEFkanVzdGVkIFJhbmQgSW5kZXggKHZzLiBMYWJlbHMpIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAqKkstTWVhbnMgKGs9OSkqKiAgICB8ICoqMC4wMjI0KiogICAgICAgfCAqKjAuMjM2KiogICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICoqREJTQ0FOKiogICAgICAgICAgIHwgKiowLjkxMzIqKiAgICAgICB8ICoqMC4yNDkqKiAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0K8J+UuSAqKkNvbmNsdXNpb246KiogV2hpbGUgY2x1c3RlcmluZyBwcm92aWRlZCBzb21lIGluc2lnaHRzIGludG8gZW1haWwgZ3JvdXBpbmdzLCBpdCB3YXMgKipub3QgZGlyZWN0bHkgdXNlZCoqIGluIHRoZSBmaW5hbCBjbGFzc2lmaWNhdGlvbiBtb2RlbCwgYXMgaXQgZGlkIG5vdCBzaWduaWZpY2FudGx5IGltcHJvdmUgTmHDr3ZlIEJheWVzIHBlcmZvcm1hbmNlLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkIuIE5hw692ZSBCYXllcyBDbGFzc2lmaWVyKioNCg0KIyMjICoqMS4gVGhlb3JldGljYWwgQmFja2dyb3VuZCoqDQoNClRoZSAqKk5hw692ZSBCYXllcyBhbGdvcml0aG0qKiBpcyBhIHByb2JhYmlsaXN0aWMgY2xhc3NpZmllciBiYXNlZCBvbiBCYXllcycgdGhlb3JlbToNCg0KJCQNClAoXHRleHR7U3BhbX0gfCBcdGV4dHtFbWFpbH0pID0gXGZyYWN7UChcdGV4dHtFbWFpbH0gfCBcdGV4dHtTcGFtfSkgUChcdGV4dHtTcGFtfSl9e1AoXHRleHR7RW1haWx9KX0NCiQkDQoNClRoaXMgYXNzdW1lcyAqKndvcmQgaW5kZXBlbmRlbmNlKiosIG1ha2luZyBpdCBjb21wdXRhdGlvbmFsbHkgZWZmaWNpZW50IGZvciB0ZXh0IGNsYXNzaWZpY2F0aW9uLg0KDQojIyMgKioyLiBNb2RlbCBDb25maWd1cmF0aW9uKioNCg0KLSAgICoqTXVsdGlub21pYWwgTmHDr3ZlIEJheWVzIChNTkIpKiogd2FzIGNob3NlbiBmb3IgdGV4dCBjbGFzc2lmaWNhdGlvbi4NCi0gICAqKkxhcGxhY2Ugc21vb3RoaW5nKiogd2FzIGFwcGxpZWQgdG8gYXZvaWQgemVybyBwcm9iYWJpbGl0aWVzLg0KLSAgICoqSHlwZXJwYXJhbWV0ZXIgdHVuaW5nKiogd2FzIHBlcmZvcm1lZCB1c2luZyAqKkdyaWQgU2VhcmNoKiouDQoNCiMjIyMgKipPcHRpbWFsIEh5cGVycGFyYW1ldGVycyoqDQoNCnwgSHlwZXJwYXJhbWV0ZXIgICAgICAgICAgICAgICAgfCBCZXN0IFZhbHVlIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgKipBbHBoYSAoTGFwbGFjZSBTbW9vdGhpbmcpKiogfCAqKjAuMDEqKiAgIHwNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipDLiBDcm9zcy1WYWxpZGF0aW9uIEFwcHJvYWNoKioNCg0KVG8gZW5zdXJlIHJvYnVzdG5lc3MsIHdlIHVzZWQgKioxMC1Gb2xkIFN0cmF0aWZpZWQgQ3Jvc3MtVmFsaWRhdGlvbioqLCBtZWFuaW5nOiAxLg0KVGhlIGRhdGFzZXQgd2FzIHNwbGl0IGludG8gKioxMCBlcXVhbCBwYXJ0cyoqLg0KMi4NCkVhY2ggZm9sZCB3YXMgdXNlZCBhcyBhICoqdGVzdCBzZXQqKiBvbmNlLCB3aGlsZSB0aGUgcmVtYWluaW5nIDkgd2VyZSB1c2VkIGZvciB0cmFpbmluZy4NCjMuDQpUaGlzIHJlc3VsdGVkIGluICoqMTAgZGlmZmVyZW50IGFjY3VyYWN5IHNjb3JlcyoqLCB3aGljaCB3ZXJlIGF2ZXJhZ2VkIGZvciBmaW5hbCBldmFsdWF0aW9uLg0KDQrwn5S5ICoqQ3Jvc3MtVmFsaWRhdGVkIEFjY3VyYWN5Kio6ICoqOTguMzUlIMKxIDAuMjElKipcDQrwn5S5ICoqU3RhbmRhcmQgRGV2aWF0aW9uKio6ICoqMC4yMSUqKiAoSW5kaWNhdGluZyBjb25zaXN0ZW50IG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBmb2xkcykNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipELiBIeXBlcnBhcmFtZXRlciBUdW5pbmcgYW5kIE1vZGVsIFNlbGVjdGlvbioqDQoNCkEgKipHcmlkIFNlYXJjaCoqIHdhcyBjb25kdWN0ZWQgZm9yICoqYWxwaGEgKExhcGxhY2Ugc21vb3RoaW5nKSoqIHRvIG9wdGltaXplIHBlcmZvcm1hbmNlLg0KDQojIyMgKipHcmlkIFNlYXJjaCBSZXN1bHRzKioNCg0KfCBBbHBoYSAgICAgfCBNZWFuIEFjY3VyYWN5IHwNCnwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18DQp8ICoqMC4wMDEqKiB8ICoqOTguNDAlKiogICAgfA0KfCAqKjAuMDEqKiAgfCAqKjk4LjM1JSoqICAgIHwNCnwgKiowLjEqKiAgIHwgKio5OC4zMCUqKiAgICB8DQoNCvCflLkgKipGaW5hbCBDaG9pY2UqKjogKipBbHBoYSA9IDAuMDEqKiAoQmFsYW5jZWQgZ2VuZXJhbGl6YXRpb24gYW5kIHBlcmZvcm1hbmNlKQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqU3VtbWFyeSBvZiBNZXRob2RvbG9neSoqDQoNCnwgU3RlcCAgICAgICAgICAgICAgICAgICAgICB8IERldGFpbHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAqKkNsdXN0ZXJpbmcqKiAgICAgICAgICAgIHwgVXNlZCBmb3IgZXhwbG9yYXRvcnkgYW5hbHlzaXMgKEstTWVhbnMsIERCU0NBTikgfA0KfCAqKk5hw692ZSBCYXllcyoqICAgICAgICAgICB8IENob3NlbiBmb3IgZmluYWwgY2xhc3NpZmljYXRpb24gICAgICAgICAgICAgICAgIHwNCnwgKipDcm9zcy1WYWxpZGF0aW9uKiogICAgICB8ICoqMTAtRm9sZCBTdHJhdGlmaWVkKiogdG8gZW5zdXJlIGdlbmVyYWxpemF0aW9uIHwNCnwgKipIeXBlcnBhcmFtZXRlciBUdW5pbmcqKiB8ICoqR3JpZCBTZWFyY2gqKiBmb3Igb3B0aW1hbCBzbW9vdGhpbmcgICAgICAgICAgIHwNCg0KIyMjICoqUmVzdWx0cyBhbmQgRXZhbHVhdGlvbioqDQoNCkluIHRoaXMgc2VjdGlvbiwgd2UgcHJlc2VudCB0aGUgKipwZXJmb3JtYW5jZSBldmFsdWF0aW9uKiogb2Ygb3VyIHNwYW0gY2xhc3NpZmljYXRpb24gbW9kZWxzLg0KVGhlIHJlc3VsdHMgaW5jbHVkZSBrZXkgKipjbGFzc2lmaWNhdGlvbiBtZXRyaWNzKiosICoqY29uZnVzaW9uIG1hdHJpY2VzKiosIGFuZCAqKlJPQy1BVUMgYW5hbHlzaXMqKiB0byBhc3Nlc3MgdGhlIGVmZmVjdGl2ZW5lc3Mgb2Ygb3VyIGFwcHJvYWNoLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkEuIFBlcmZvcm1hbmNlIE1ldHJpY3MqKg0KDQpUaGUgcHJpbWFyeSBldmFsdWF0aW9uIG1ldHJpY3MgaW5jbHVkZTpcDQoxLg0KKipBY2N1cmFjeSoqOiBPdmVyYWxsIGNvcnJlY3RuZXNzIG9mIHByZWRpY3Rpb25zLlwNCjIuDQoqKlByZWNpc2lvbioqOiBIb3cgbWFueSBwcmVkaWN0ZWQgc3BhbSBlbWFpbHMgYXJlIGFjdHVhbGx5IHNwYW0/XA0KMy4NCioqUmVjYWxsKio6IEhvdyBtYW55IGFjdHVhbCBzcGFtIGVtYWlscyB3ZXJlIGNvcnJlY3RseSBpZGVudGlmaWVkP1wNCjQuDQoqKkYxLVNjb3JlKio6IEhhcm1vbmljIG1lYW4gb2YgcHJlY2lzaW9uIGFuZCByZWNhbGwuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqMS4gTmHDr3ZlIEJheWVzIFBlcmZvcm1hbmNlKioNCg0KIyMjIyBBZnRlciAqKjEwLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbioqLCB3ZSBvYnRhaW5lZCB0aGUgZm9sbG93aW5nIHJlc3VsdHM6DQoNCnwgTWV0cmljICAgICAgICAgICAgICAgICAgICAgfCBTY29yZSAoJSkgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgKipBY2N1cmFjeSoqICAgICAgICAgICAgICAgfCAqKjk4LjM1KiogwrEgMC4yMSB8DQp8ICoqUHJlY2lzaW9uIChTcGFtIENsYXNzKSoqIHwgKio5OS4xKiogICAgICAgICB8DQp8ICoqUmVjYWxsIChTcGFtIENsYXNzKSoqICAgIHwgKio5Ny44KiogICAgICAgICB8DQp8ICoqRjEtU2NvcmUgKFNwYW0gQ2xhc3MpKiogIHwgKio5OC40KiogICAgICAgICB8DQoNCvCflLkgKipJbnRlcnByZXRhdGlvbjoqKiBOYcOvdmUgQmF5ZXMgcHJvdmlkZXMgKipoaWdoIGFjY3VyYWN5Kiogd2l0aCBhICoqc2xpZ2h0IGJpYXMgdG93YXJkcyBtaXNjbGFzc2lmeWluZyBzcGFtIGFzIG5vbi1zcGFtKiogKGxvd2VyIHJlY2FsbCkuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqMi4gQ29uZnVzaW9uIE1hdHJpeCoqDQoNClRoZSBjb25mdXNpb24gbWF0cml4ICoqdmlzdWFsaXplcyBlcnJvcnMqKiBiZXR3ZWVuIGFjdHVhbCBhbmQgcHJlZGljdGVkIGxhYmVscy4NCg0KIyMjIyAqKk5hw692ZSBCYXllcyBDb25mdXNpb24gTWF0cml4KioNCg0KfCBBY3R1YWwg4oaSIFByZWRpY3RlZCDihpMgICAgICAgfCAgICAgIEhhbSB8ICAgICBTcGFtIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLTp8LS0tLS0tLS0tOnwNCnwgKipIYW0gKExlZ2l0aW1hdGUgRW1haWwpKiogfCAqKjEzODAqKiB8ICAgKioxMioqIHwNCnwgKipTcGFtKiogICAgICAgICAgICAgICAgICAgfCAgICoqMjMqKiB8ICoqMTM2NioqIHwNCg0K8J+UuSAqKkZhbHNlIFBvc2l0aXZlcyAoMTIgZW1haWxzIG1pc2NsYXNzaWZpZWQgYXMgc3BhbSkqKiDigJMgTWluaW1hbCByaXNrIG9mICoqbG9zaW5nIGltcG9ydGFudCBlbWFpbHMqKi5cDQrwn5S5ICoqRmFsc2UgTmVnYXRpdmVzICgyMyBzcGFtIGVtYWlscyBub3QgZGV0ZWN0ZWQpKiog4oCTIFNtYWxsIHJpc2sgb2Ygc3BhbSBzbGlwcGluZyB0aHJvdWdoLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjMuIENvbXBhcmlzb24gd2l0aCBPdGhlciBNb2RlbHMqKg0KDQpXZSBjb21wYXJlICoqUmFuZG9tIEZvcmVzdCAoUkYpKiosICoqWEdCb29zdCAoWEdCKSoqLCBhbmQgKipTZWxmLVRyYWluaW5nIFhHQm9vc3QgKFNULVhHQikqKi4NCg0KfCBNb2RlbCAgICAgICAgICAgICAgICAgIHwgQWNjdXJhY3kgKCUpIHwgUHJlY2lzaW9uIHwgUmVjYWxsICAgfCBGMS1TY29yZSB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS18LS0tLS0tLS0tLXwNCnwgKipOYcOvdmUgQmF5ZXMqKiAgICAgICAgfCAqKjk4LjM1KiogICAgfCAqKjk5LjEqKiAgfCAqKjk3LjgqKiB8ICoqOTguNCoqIHwNCnwgKipSYW5kb20gRm9yZXN0IChSRikqKiB8ICoqOTcuOCoqICAgICB8ICoqOTUuMyoqICB8ICoqOTQuNioqIHwgKio5NS4wKiogfA0KfCAqKlhHQm9vc3QgKFhHQikqKiAgICAgIHwgKio5OS4yKiogICAgIHwgKio5OS40KiogIHwgKio5OS4xKiogfCAqKjk5LjMqKiB8DQp8ICoqU2VsZi1UcmFpbmluZyBYR0IqKiAgfCAqKjk5LjYqKiAgICAgfCAqKjk5LjcqKiAgfCAqKjk5LjYqKiB8ICoqOTkuNyoqIHwNCg0K8J+UuSAqKkJlc3QgUGVyZm9ybWluZyBNb2RlbCoqOiAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCAoOTkuNiUgQWNjdXJhY3kpKipcDQrwn5S5ICoqWEdCb29zdCBhbHNvIG91dHBlcmZvcm1lZCBOYcOvdmUgQmF5ZXMqKiwgYnV0IE5hw692ZSBCYXllcyByZW1haW5zIGEgKipzdHJvbmcgYmFzZWxpbmUgY2xhc3NpZmllcioqLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjQuIFJPQyBDdXJ2ZSBhbmQgQVVDIEFuYWx5c2lzKioNCg0KKipSZWNlaXZlciBPcGVyYXRpbmcgQ2hhcmFjdGVyaXN0aWMgKFJPQykgQ3VydmUqKiB2aXN1YWxpemVzIGhvdyB3ZWxsIHRoZSBtb2RlbHMgZGlzdGluZ3Vpc2ggc3BhbSBmcm9tIGhhbS4NCg0KLSAgICoqQVVDIChBcmVhIFVuZGVyIEN1cnZlKSoqIG1lYXN1cmVzIGRpc2NyaW1pbmF0aW9uIGFiaWxpdHk6DQogICAgLSAgICoqTmHDr3ZlIEJheWVzIEFVQyoqID0gKiowLjk4NyoqDQogICAgLSAgICoqWEdCb29zdCBBVUMqKiA9ICoqMC45OTkqKg0KICAgIC0gICAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCBBVUMqKiA9ICoqMS4wMDAqKg0KDQoqKk9ic2VydmF0aW9uczoqKlwNCi0gKipTZWxmLVRyYWluaW5nIFhHQiBpcyBuZWFyLXBlcmZlY3QqKiB3aXRoIEFVQyAqKjEuMDAqKi5cDQotICoqTmHDr3ZlIEJheWVzIHN0aWxsIHBlcmZvcm1zIHdlbGwqKiBidXQgaGFzIGEgKipzbGlnaHRseSBsb3dlciBBVUMqKiB0aGFuIFhHQm9vc3QgbW9kZWxzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkIuIENvc3Qgb2YgTWlzY2xhc3NpZmljYXRpb24qKg0KDQojIyMgKipGYWxzZSBQb3NpdGl2ZXMgdnMuIEZhbHNlIE5lZ2F0aXZlcyoqDQoNCi0gICAqKkZhbHNlIFBvc2l0aXZlcyAoSGFtIG1pc2NsYXNzaWZpZWQgYXMgU3BhbSkqKiDihpIgUmlzayBvZiAqKmxvc2luZyBpbXBvcnRhbnQgZW1haWxzKiouDQotICAgKipGYWxzZSBOZWdhdGl2ZXMgKFNwYW0gY2xhc3NpZmllZCBhcyBIYW0pKiog4oaSIFJpc2sgb2YgKipzcGFtIGJ5cGFzc2luZyB0aGUgZmlsdGVyKiouDQoNCioqVHJhZGUtT2ZmIERlY2lzaW9uKio6XA0KR2l2ZW4gdGhlICoqaW1wb3J0YW5jZSBvZiBhdm9pZGluZyBsb3N0IGxlZ2l0aW1hdGUgZW1haWxzKiosIHdlIHByaW9yaXRpemUgKipyZWR1Y2luZyBmYWxzZSBwb3NpdGl2ZXMqKi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipDLiBNb2RlbCBTZWxlY3Rpb24gZm9yIERlcGxveW1lbnQqKg0KDQpBZnRlciBldmFsdWF0aW5nIG11bHRpcGxlIGNsYXNzaWZpZXJzLCB0aGUgKipiZXN0IGZpbmFsIHJlY29tbWVuZGF0aW9uKiogaXM6DQoNCjEuICAqKlByaW1hcnkgTW9kZWw6KiogKipTZWxmLVRyYWluaW5nIFhHQm9vc3QqKiAoSGlnaGVzdCBhY2N1cmFjeSBhbmQgbG93ZXN0IG1pc2NsYXNzaWZpY2F0aW9uIGVycm9ycykuDQoyLiAgKipCYWNrdXAgTW9kZWw6KiogKipYR0Jvb3N0KiogKFN0aWxsIGhpZ2hseSBlZmZlY3RpdmUsIHNpbXBsZXIgdGhhbiBTZWxmLVRyYWluaW5nKS4NCjMuICAqKkJhc2VsaW5lIE1vZGVsOioqICoqTmHDr3ZlIEJheWVzKiogKEdvb2QgZm9yIGxpZ2h0d2VpZ2h0IGltcGxlbWVudGF0aW9uKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlN1bW1hcnkgb2YgRXZhbHVhdGlvbioqDQoNCnwgTW9kZWwgICAgICAgICAgICAgICAgIHwgQWNjdXJhY3kgKCUpIHwgQmVzdCBVc2UgQ2FzZSAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAqKk5hw692ZSBCYXllcyoqICAgICAgIHwgKio5OC4zNSoqICAgIHwgRmFzdCwgbGlnaHR3ZWlnaHQgYmFzZWxpbmUgbW9kZWwgICAgICB8DQp8ICoqUmFuZG9tIEZvcmVzdCoqICAgICB8ICoqOTcuOCoqICAgICB8IE1vcmUgY29tcGxleCwgc2xpZ2h0bHkgbG93ZXIgYWNjdXJhY3kgfA0KfCAqKlhHQm9vc3QqKiAgICAgICAgICAgfCAqKjk5LjIqKiAgICAgfCBIaWdobHkgZWZmZWN0aXZlLCBiZXR0ZXIgdGhhbiBOQiAmIFJGIHwNCnwgKipTZWxmLVRyYWluaW5nIFhHQioqIHwgKio5OS42KiogICAgIHwgKipCZXN0IG1vZGVsLCBsb3dlc3QgZXJyb3IgcmF0ZSoqICAgICB8DQoNCioqQ29uY2x1c2lvbjoqKiAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCoqIGlzIHRoZSBvcHRpbWFsIGNob2ljZSBmb3IgcHJvZHVjdGlvbiBkZXBsb3ltZW50Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqRGlzY3Vzc2lvbiBhbmQgSW5zaWdodHMqKg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGFuYWx5emUgdGhlIGltcGFjdCBvZiAqKmNsdXN0ZXJpbmcqKiwgdGhlIGJhbGFuY2UgYmV0d2VlbiAqKmZhbHNlIHBvc2l0aXZlcyB2cy4gZmFsc2UgbmVnYXRpdmVzKiosIGFuZCB0aGUgKipsaW1pdGF0aW9ucyoqIG9mIG91ciBjdXJyZW50IG1vZGVsLg0KV2UgYWxzbyBkaXNjdXNzIHBvdGVudGlhbCAqKmVuaGFuY2VtZW50cyoqIGZvciBmdXR1cmUgaXRlcmF0aW9ucy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKipBLiBJbXBhY3Qgb2YgQ2x1c3RlcmluZyBvbiBQZXJmb3JtYW5jZSoqDQoNCiMjIyAqKkRpZCBjbHVzdGVyaW5nIGltcHJvdmUgY2xhc3NpZmljYXRpb24gcmVzdWx0cz8qKg0KDQoxLiAgKipLLU1lYW5zIChCZXN0IGsgPSA5KSoqDQogICAgLSAgICoqU2lsaG91ZXR0ZSBTY29yZToqKiAqKjAuMDIyNCoqICh2ZXJ5IGxvd+KAlGluZGljYXRpbmcgcG9vciBjbHVzdGVyIHNlcGFyYWJpbGl0eSkuDQogICAgLSAgICoqQWRqdXN0ZWQgUmFuZCBJbmRleCAoQVJJKToqKiAqKjAuMjM2KiogKHdlYWsgYWdyZWVtZW50IHdpdGggYWN0dWFsIGxhYmVscykuDQoyLiAgKipEQlNDQU4gKEJlc3QgzrUgPSAwLjUsIG1pbl9zYW1wbGVzID0gNSkqKg0KICAgIC0gICAqKlNpbGhvdWV0dGUgU2NvcmU6KiogKiowLjkxMyoqIChzdHJvbmcgaW50ZXJuYWwgY29oZXNpb24pLg0KICAgIC0gICAqKkFSSToqKiAqKjAuMjQ5KiogKHNsaWdodGx5IGJldHRlciBhZ3JlZW1lbnQgd2l0aCB0cnVlIGxhYmVscyB0aGFuIEstTWVhbnMpLg0KDQrwn5S5ICoqT2JzZXJ2YXRpb246KipcDQotICoqREJTQ0FOIHBlcmZvcm1lZCBiZXR0ZXIqKiB0aGFuIEstTWVhbnMgaW4gZ3JvdXBpbmcgc3BhbSBhbmQgbm9uLXNwYW0gZW1haWxzLlwNCi0gKipIb3dldmVyLCBjbHVzdGVyaW5nIGFsb25lIHdhcyBpbnN1ZmZpY2llbnQqKiBmb3IgYWNjdXJhdGUgc3BhbSBkZXRlY3Rpb24uDQotICoqRmluYWwgRGVjaXNpb246KiogQ2x1c3RlcmluZyAqKndhcyBub3QgZGlyZWN0bHkgdXNlZCBpbiBjbGFzc2lmaWNhdGlvbioqIGJ1dCBoZWxwZWQgKip1bmRlcnN0YW5kIHNwYW0gZGlzdHJpYnV0aW9uKiouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqQi4gRmFsc2UgUG9zaXRpdmVzIHZzLiBGYWxzZSBOZWdhdGl2ZXMqKg0KDQpTaW5jZSBvdXIgZ29hbCBpcyB0byAqKm1pbmltaXplIGRpc3J1cHRpb24gdG8gYnVzaW5lc3Mgb3BlcmF0aW9ucyoqLCB3ZSBhbmFseXplIHRoZSB0cmFkZS1vZmY6DQoNCi0gICAqKkZhbHNlIFBvc2l0aXZlcyAoRlApOioqIExlZ2l0aW1hdGUgZW1haWxzIG1pc2NsYXNzaWZpZWQgYXMgc3BhbS4NCiAgICAtICAgKipSaXNrOioqIExvc3Mgb2YgaW1wb3J0YW50IGJ1c2luZXNzIGVtYWlscy5cDQogICAgLSAgICoqTWl0aWdhdGlvbiBTdHJhdGVneToqKiBGYXZvciBoaWdoICoqcHJlY2lzaW9uKiogYW5kIGFsbG93IGEgc21hbGwgYW1vdW50IG9mIHNwYW0gdGhyb3VnaC4NCi0gICAqKkZhbHNlIE5lZ2F0aXZlcyAoRk4pOioqIFNwYW0gZW1haWxzIG1pc2NsYXNzaWZpZWQgYXMgaGFtLg0KICAgIC0gICAqKlJpc2s6KiogVXNlcnMgcmVjZWl2ZSB1bndhbnRlZCBzcGFtLlwNCiAgICAtICAgKipNaXRpZ2F0aW9uIFN0cmF0ZWd5OioqIFByaW9yaXRpemUgKipoaWdoIHJlY2FsbCoqIHdoaWxlIGtlZXBpbmcgRlAgbG93Lg0KDQpTKipEZWNpc2lvbjoqKlwNCldlIG9wdGltaXplZCAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCoqIHRvICoqcmVkdWNlIGZhbHNlIHBvc2l0aXZlcyoqLCBlbnN1cmluZyBpbXBvcnRhbnQgZW1haWxzIGFyZSBub3QgbWlzdGFrZW5seSBmbGFnZ2VkLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkMuIExpbWl0YXRpb25zIG9mIHRoZSBDdXJyZW50IE1vZGVsKioNCg0KIyMjICoqMS4gRGF0YXNldCBBZ2UgYW5kIFJlcHJlc2VudGF0aXZlbmVzcyoqDQoNCioqQ2hhbGxlbmdlOioqXA0KLSBUaGUgKipTcGFtQXNzYXNzaW4gZGF0YXNldCoqIG1heSBub3QgZnVsbHkgcmVmbGVjdCBtb2Rlcm4gc3BhbSAoZS5nLiwgKipwaGlzaGluZywgZGVlcGZha2Ugc2NhbXMsIGFuZCBBSS1nZW5lcmF0ZWQgc3BhbSoqKS4NCi0gKipTcGFtIGV2b2x2ZXMgb3ZlciB0aW1lKiosIHJlcXVpcmluZyBjb250aW51b3VzIHVwZGF0ZXMuDQoNCioqRnV0dXJlIEltcHJvdmVtZW50OioqXA0KLSAqKlJlZ3VsYXIgbW9kZWwgcmV0cmFpbmluZyoqIG9uIHVwZGF0ZWQgc3BhbSBkYXRhc2V0cyAoZS5nLiwgRW5yb24sIHJlYWwtdGltZSBjb21wYW55IGVtYWlscykuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKioyLiBUZXh0IFJlcHJlc2VudGF0aW9uIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nKioNCg0KKipDaGFsbGVuZ2U6KipcDQotICoqQmFnLW9mLVdvcmRzIChCT1cpKiogYW5kICoqVEYtSURGKiogZG8gbm90IGNhcHR1cmUgY29udGV4dHVhbCBtZWFuaW5nIChlLmcuLCBzYXJjYXNtLCBpbnRlbnQpLg0KDQoqKkZ1dHVyZSBJbXByb3ZlbWVudDoqKlwNCi0gKipBZHZhbmNlZCBOTFAgdGVjaG5pcXVlcyoqIChlLmcuLCBXb3JkMlZlYywgQkVSVCBlbWJlZGRpbmdzKSBjb3VsZCAqKmltcHJvdmUgY2xhc3NpZmljYXRpb24qKiBieSB1bmRlcnN0YW5kaW5nIHdvcmQgcmVsYXRpb25zaGlwcy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKjMuIE1vZGVsIENvbXBsZXhpdHkgdnMuIEludGVycHJldGFiaWxpdHkqKg0KDQoqKkNoYWxsZW5nZToqKlwNCi0gKipOYcOvdmUgQmF5ZXMgaXMgZWFzeSB0byBpbnRlcnByZXQgYnV0IGxhY2tzIGZsZXhpYmlsaXR5KiouXA0KLSAqKlhHQm9vc3QgaXMgbW9yZSBjb21wbGV4IGJ1dCBoYXJkZXIgdG8gZXhwbGFpbioqIHRvIG5vbi10ZWNobmljYWwgdXNlcnMuDQoNCioqRnV0dXJlIEltcHJvdmVtZW50OioqXA0KLSAqKkV4cGxhaW5hYmxlIEFJIChYQUkpIHRlY2huaXF1ZXMqKiAoZS5nLiwgU0hBUCB2YWx1ZXMsIExJTUUpIGNhbiBpbXByb3ZlIG1vZGVsIGludGVycHJldGFiaWxpdHkuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqRC4gVmlzdWFsaXphdGlvbnMqKg0KDQohW10obWFydHJpY2VzLnBuZykNCg0KIVtdKDIucG5nKQ0KDQohW10oMy5wbmcpDQoNCiFbXSg0LnBuZykNCg0KIVtdKDYucG5nKQ0KDQohW10oOC5wbmcpDQoNCiFbXSg5LnBuZykNCg0KIVtdKDEwLnBuZykNCg0KIVtdKDExLnBuZykNCg0KIyMjICoqU3VtbWFyeSBhbmQgQW5hbHlzaXMgb2YgUGxvdHMgYW5kIFZpc3VhbGl6YXRpb25zKioNCg0KIyMjIyAqKjEuIENvbmZ1c2lvbiBNYXRyaWNlcyBmb3IgMTAtRm9sZCBDcm9zcy1WYWxpZGF0aW9uKioNCg0KLSAgIEVhY2ggY29uZnVzaW9uIG1hdHJpeCByZXByZXNlbnRzIHRoZSBjbGFzc2lmaWNhdGlvbiBwZXJmb3JtYW5jZSBhY3Jvc3MgZGlmZmVyZW50IGZvbGRzIGluIHRoZSBjcm9zcy12YWxpZGF0aW9uIHByb2Nlc3MuDQotICAgKipPYnNlcnZhdGlvbjoqKg0KICAgIC0gICBUaGUgKipmYWxzZSBwb3NpdGl2ZXMgKGhhbSBtaXNjbGFzc2lmaWVkIGFzIHNwYW0pKiogcmFuZ2UgYmV0d2VlbiAqKjI3IHRvIDQzKiogcGVyIGZvbGQuDQogICAgLSAgIFRoZSAqKmZhbHNlIG5lZ2F0aXZlcyAoc3BhbSBtaXNjbGFzc2lmaWVkIGFzIGhhbSkqKiByYW5nZSBiZXR3ZWVuICoqMjkgdG8gNDIqKiBwZXIgZm9sZC4NCiAgICAtICAgVGhlIGNsYXNzaWZpZXIgc2hvd3MgKipoaWdoIHByZWNpc2lvbioqLCBtZWFuaW5nIHNwYW0gZGV0ZWN0aW9uIGlzIHJlbGlhYmxlLCBidXQgdGhlcmUgaXMgc29tZSAqKnZhcmlhbmNlIGluIHJlY2FsbCoqIChhYmlsaXR5IHRvIGRldGVjdCBhbGwgc3BhbSBlbWFpbHMpLg0KICAgIC0gICAqKkJhbGFuY2VkIGNsYXNzaWZpY2F0aW9uIHBlcmZvcm1hbmNlKiogYWNyb3NzIGZvbGRzLCBpbmRpY2F0aW5nIGEgd2VsbC1nZW5lcmFsaXplZCBtb2RlbC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKioyLiBST0MgQ3VydmU6IFJhbmRvbSBGb3Jlc3QgdnMuIFhHQm9vc3QqKg0KDQotICAgVGhlICoqUk9DIGN1cnZlKiogZXZhbHVhdGVzIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiAqKmZhbHNlIHBvc2l0aXZlcyoqIGFuZCAqKnRydWUgcG9zaXRpdmVzKiouDQotICAgKipPYnNlcnZhdGlvbjoqKg0KICAgIC0gICBCb3RoICoqUmFuZG9tIEZvcmVzdCBhbmQgWEdCb29zdCBhY2hpZXZlIGFuIEFVQyBvZiAxLjAwKiosIGluZGljYXRpbmcgbmVhci1wZXJmZWN0IGNsYXNzaWZpY2F0aW9uLg0KICAgIC0gICAqKlhHQm9vc3QgaGFzIGEgc3RlZXBlciBhc2NlbnQqKiwgbWVhbmluZyBpdCBhY2hpZXZlcyBoaWdoZXIgdHJ1ZSBwb3NpdGl2ZSByYXRlcyB3aXRoIGZld2VyIGZhbHNlIHBvc2l0aXZlcyBjb21wYXJlZCB0byBSYW5kb20gRm9yZXN0Lg0KICAgIC0gICAqKkNvbmNsdXNpb246KiogWEdCb29zdCBpcyBsaWtlbHkgdGhlIG1vcmUgb3B0aW1hbCBjaG9pY2UgZHVlIHRvIGl0cyBpbXByb3ZlZCBwcmVjaXNpb24gYXQgbG93ZXIgZmFsc2UgcG9zaXRpdmUgcmF0ZXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqMy4gQ29uZnVzaW9uIE1hdHJpY2VzIGZvciBSYW5kb20gRm9yZXN0IHZzLiBYR0Jvb3N0KioNCg0KLSAgIFRoZSAqKlJhbmRvbSBGb3Jlc3QgbW9kZWwqKjoNCiAgICAtICAgKio2OCBzcGFtIGVtYWlscyB3ZXJlIG1pc2NsYXNzaWZpZWQgYXMgaGFtKiosIG1lYW5pbmcgaXQgc3RydWdnbGVzIHdpdGggcmVjYWxsLg0KICAgIC0gICAqKjcgbGVnaXRpbWF0ZSBlbWFpbHMgd2VyZSBmYWxzZWx5IGNsYXNzaWZpZWQgYXMgc3BhbSoqLCBpbmRpY2F0aW5nIGdvb2QgcHJlY2lzaW9uLg0KLSAgIFRoZSAqKlhHQm9vc3QgbW9kZWwqKjoNCiAgICAtICAgKipPbmx5IDIgc3BhbSBlbWFpbHMgd2VyZSBtaXNjbGFzc2lmaWVkIGFzIGhhbSoqLCBzaWduaWZpY2FudGx5IGltcHJvdmluZyByZWNhbGwuDQogICAgLSAgICoqMjAgbGVnaXRpbWF0ZSBlbWFpbHMgd2VyZSBtaXNjbGFzc2lmaWVkIGFzIHNwYW0qKiwgbWVhbmluZyBYR0Jvb3N0IHByaW9yaXRpemVzIGNhcHR1cmluZyBzcGFtIGJ1dCBhdCB0aGUgY29zdCBvZiBzbGlnaHRseSBsb3dlciBwcmVjaXNpb24uDQotICAgKipDb25jbHVzaW9uOioqIFhHQm9vc3QgcHJvdmlkZXMgKipiZXR0ZXIgcmVjYWxsKiosIG1ha2luZyBpdCB0aGUgcHJlZmVyYWJsZSBtb2RlbCBmb3IgZW5zdXJpbmcgYWxsIHNwYW0gaXMgY2FwdHVyZWQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqNC4gUENBLUJhc2VkIENsdXN0ZXJpbmcgVmlzdWFsaXphdGlvbnMqKg0KDQotICAgKipMZWZ0OiBLLU1lYW5zIENsdXN0ZXJpbmcsIFJpZ2h0OiBEQlNDQU4gQ2x1c3RlcmluZyoqDQotICAgKipPYnNlcnZhdGlvbjoqKg0KICAgIC0gICAqKkstTWVhbnMgY2x1c3RlcmluZyBzaG93cyBkaXN0aW5jdCBjbHVzdGVycyoqLCBidXQgdGhlcmUgaXMgc2lnbmlmaWNhbnQgb3ZlcmxhcCwgbWVhbmluZyBzcGFtIGFuZCBoYW0gZW1haWxzIGRvIG5vdCBzZXBhcmF0ZSBwZXJmZWN0bHkuDQogICAgLSAgICoqREJTQ0FOIHByb2R1Y2VzIG1vcmUgcmVmaW5lZCBjbHVzdGVycyB3aXRoIGZld2VyIG5vaXN5IHBvaW50cyoqLCBtYWtpbmcgaXQgbW9yZSBlZmZlY3RpdmUgYXQgZGV0ZWN0aW5nIG5hdHVyYWwgZ3JvdXBpbmdzIGluIHNwYW0gY2xhc3NpZmljYXRpb24uDQogICAgLSAgICoqQ29uY2x1c2lvbjoqKiBDbHVzdGVyaW5nIGlzIHVzZWZ1bCBmb3IgZXhwbG9yYXRvcnkgYW5hbHlzaXMgYnV0IG5vdCBvcHRpbWFsIGFzIGEgc3RhbmRhbG9uZSBjbGFzc2lmaWVyLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjUuIENvbmZ1c2lvbiBNYXRyaXg6IE5hw692ZSBCYXllcyoqDQoNCi0gICAqKk9ic2VydmF0aW9uOioqDQogICAgLSAgICoqMTIgZmFsc2UgcG9zaXRpdmVzKiogKGhhbSBtaXNjbGFzc2lmaWVkIGFzIHNwYW0pLg0KICAgIC0gICAqKjIzIGZhbHNlIG5lZ2F0aXZlcyoqIChzcGFtIG1pc2NsYXNzaWZpZWQgYXMgaGFtKS4NCiAgICAtICAgKipPdmVyYWxsLCBOYcOvdmUgQmF5ZXMgaXMgc3Ryb25nIGJ1dCBsZXNzIHByZWNpc2UgdGhhbiBYR0Jvb3N0KiouDQogICAgLSAgICoqQ29uY2x1c2lvbjoqKiBOYcOvdmUgQmF5ZXMgaXMgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudCBidXQgbm90IGFzIHJvYnVzdCBhcyBlbnNlbWJsZSBtZXRob2RzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjYuIFJPQyBDdXJ2ZSBmb3IgTmHDr3ZlIEJheWVzKioNCg0KLSAgICoqQVVDIFNjb3JlOiAxLjAwKiog4oCTIGluZGljYXRpbmcgc3Ryb25nIHNlcGFyYXRpb24gYmV0d2VlbiBzcGFtIGFuZCBoYW0uDQotICAgKipPYnNlcnZhdGlvbjoqKg0KICAgIC0gICBXaGlsZSAqKk5hw692ZSBCYXllcyBwZXJmb3JtcyB3ZWxsKiosIGl0IGxhY2tzIHRoZSAqKmZpbmUtdHVuZWQgY29udHJvbCBvdmVyIHByZWNpc2lvbiBhbmQgcmVjYWxsKiogdGhhdCBYR0Jvb3N0IHByb3ZpZGVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjcuIEdyaWQgU2VhcmNoIEhlYXRtYXAgYW5kIEFscGhhIHZzLiBBY2N1cmFjeSAoTmHDr3ZlIEJheWVzKSoqDQoNCi0gICAqKkh5cGVycGFyYW1ldGVyIHR1bmluZyByZXN1bHRzIGZvciBMYXBsYWNlIHNtb290aGluZyAoYWxwaGEpLioqDQotICAgKipPYnNlcnZhdGlvbjoqKg0KICAgIC0gICAqKkFscGhhID0gMC4wMDEgYWNoaWV2ZXMgdGhlIGhpZ2hlc3QgYWNjdXJhY3kgKFx+OTguMzUlKSoqLg0KDQogICAgLSAgICoqSW5jcmVhc2luZyBhbHBoYSB0b28gbXVjaCAoZS5nLiwgMTApIGRyYXN0aWNhbGx5IHJlZHVjZXMgYWNjdXJhY3kgKFx+OTYuOCUpKiouDQoNCiAgICAtICAgKipDb25jbHVzaW9uOioqIFByb3BlciBoeXBlcnBhcmFtZXRlciB0dW5pbmcgaXMgY3J1Y2lhbOKAlGFscGhhIHZhbHVlcyBhcm91bmQgKiowLjAwMSB0byAwLjAxKiogd29yayBiZXN0IGZvciBOYcOvdmUgQmF5ZXMuDQoNCiAgICAtICAgDQoNCiAgICAgICAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKio4LiBDb25mdXNpb24gTWF0cml4OiBTZWxmLVRyYWluaW5nIFhHQm9vc3QqKg0KDQotICAgKipUaGlzIG1vZGVsIGhhcyAwIGZhbHNlIG5lZ2F0aXZlcyoqLCBtZWFuaW5nICoqZXZlcnkgc3BhbSBlbWFpbCB3YXMgY29ycmVjdGx5IGNsYXNzaWZpZWQqKi4NCg0KLSAgICoqT25seSA3IGZhbHNlIHBvc2l0aXZlcyoqLCBtYWtpbmcgaXQgdGhlIG1vc3QgYWNjdXJhdGUgbW9kZWwgb3ZlcmFsbC4NCg0KICAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqOS4gVG9wIEtleXdvcmRzIGluIFNwYW0gRGV0ZWN0aW9uKioNCg0KLSAgIEZlYXR1cmVzIHdpdGggdGhlIGhpZ2hlc3Qgc3BhbSBwcm9iYWJpbGl0eSBpbmNsdWRlICoqImluc3VyYW5jZSwiICJsZW5kZXIsIiAiYW5udWl0eSwiIGFuZCBmaW5hbmNpYWwgdGVybXMqKi4NCg0KLSAgIFRoZXNlIGluc2lnaHRzIGFsaWduIHdpdGggY29tbW9uIHNwYW0gY2F0ZWdvcmllcyBsaWtlICoqcGhpc2hpbmcsIHNjYW1zLCBhbmQgZmluYW5jaWFsIGZyYXVkKiouDQoNCiAgICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKjEwLiBUb3AgU2VuZGVyIERvbWFpbnMgZm9yIFNwYW0qKg0KDQotICAgRG9tYWlucyBsaWtlICoqZmxhc2htYWlsLmNvbSwgZnJlZXVrLmNvbSwgYW5kIGZyZWlnaHRtYXJ0LmNvbSoqIGFyZSBoZWF2aWx5IGFzc29jaWF0ZWQgd2l0aCBzcGFtLg0KLSAgIFRoaXMgc3VnZ2VzdHMgdGhhdCAqKmVtYWlsIG1ldGFkYXRhIGNhbiBlbmhhbmNlIHNwYW0gY2xhc3NpZmljYXRpb24qKiBhbG9uZ3NpZGUgdGV4dC1iYXNlZCBmZWF0dXJlcy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgKioxMS4gVG9wIFNwYW0gV29yZHMgYnkgTG9nLUxpa2VsaWhvb2QqKg0KDQotICAgSGlnaCBsb2ctbGlrZWxpaG9vZCB3b3JkcyBpbmNsdWRlICoq4oCcc3BhbWFzc2Fzc2luLOKAnSDigJxpbnN1cmFuY2Us4oCdIOKAnGZlYXR1cmUgcGFjayzigJ0gYW5kIOKAnG11bHRpbGV2ZWzigJ0qKi4NCi0gICBTcGFtIGNsYXNzaWZpZXJzIGNhbiBiZW5lZml0IGZyb20gKipkeW5hbWljIGtleXdvcmQgdXBkYXRlcyoqIHRvIHN0YXkgYWhlYWQgb2YgZXZvbHZpbmcgc3BhbSB0YWN0aWNzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjICoqT3ZlcmFsbCBDb25jbHVzaW9ucyoqDQoNCjEuICAqKlNlbGYtVHJhaW5pbmcgWEdCb29zdCBpcyB0aGUgYmVzdCBtb2RlbCoqLCBhY2hpZXZpbmcgdGhlIGhpZ2hlc3QgYWNjdXJhY3kgd2l0aCAqKjAgZmFsc2UgbmVnYXRpdmVzIGFuZCBvbmx5IDcgZmFsc2UgcG9zaXRpdmVzKiouDQoyLiAgKipOYcOvdmUgQmF5ZXMgaXMgYSBzdHJvbmcgYmFzZWxpbmUgYnV0IHN0cnVnZ2xlcyB3aXRoIGFtYmlndW91cyBjYXNlcyoqLCBtYWtpbmcgaXQgYSBsZXNzIG9wdGltYWwgY2hvaWNlLg0KMy4gICoqUmFuZG9tIEZvcmVzdCBoYXMgaGlnaGVyIGZhbHNlIG5lZ2F0aXZlcyoqLCBtYWtpbmcgaXQgbGVzcyByZWxpYWJsZSB0aGFuIFhHQm9vc3QuDQo0LiAgKipDbHVzdGVyaW5nIChEQlNDQU4pIHByb3ZpZGVzIHNvbWUgaW5zaWdodHMgYnV0IGRvZXMgbm90IHNpZ25pZmljYW50bHkgZW5oYW5jZSBjbGFzc2lmaWNhdGlvbiBwZXJmb3JtYW5jZSoqLg0KNS4gICoqRmVhdHVyZSBlbmdpbmVlcmluZyAoc2VuZGVyIGRvbWFpbnMsIGtleXdvcmQgcHJvYmFiaWxpdGllcywgbG9nLWxpa2VsaWhvb2QpIGNhbiBmdXJ0aGVyIHJlZmluZSBzcGFtIGRldGVjdGlvbiBtb2RlbHMqKi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKkZpbmFsIFJlY29tbWVuZGF0aW9uKioNCg0KLSAgICoqRGVwbG95IFNlbGYtVHJhaW5pbmcgWEdCb29zdCBmb3IgcmVhbC13b3JsZCBzcGFtIGZpbHRlcmluZyoqIGR1ZSB0byBpdHMgc3VwZXJpb3IgcmVjYWxsIGFuZCBwcmVjaXNpb24uDQotICAgKipQZXJpb2RpY2FsbHkgdXBkYXRlIHNwYW0ga2V5d29yZCBsaXN0cyBhbmQgc2VuZGVyIGRvbWFpbiBibGFja2xpc3RzKiogZm9yIGFkYXB0aXZlIGZpbHRlcmluZy4NCi0gICAqKkNvbnNpZGVyIGludGVncmF0aW5nIGNsdXN0ZXJpbmcgaW5zaWdodHMgaW50byBmZWF0dXJlIGVuZ2luZWVyaW5nKiogZm9yIGZ1cnRoZXIgaW1wcm92ZW1lbnRzLg0KDQojIyMgDQoNCiMjIyAqKkZpbmFsIFRha2Vhd2F5cyoqDQoNCi0gICAqKlhHQm9vc3Qgb3V0cGVyZm9ybXMgYm90aCBOYcOvdmUgQmF5ZXMgYW5kIFJhbmRvbSBGb3Jlc3QqKiwgb2ZmZXJpbmcgc3VwZXJpb3IgcmVjYWxsIGFuZCBoaWdoIEFVQy4NCi0gICAqKk5hw692ZSBCYXllcyByZW1haW5zIGEgc3Ryb25nIGJhc2VsaW5lIGNsYXNzaWZpZXIqKiBidXQgbGFja3MgdGhlIGZsZXhpYmlsaXR5IG9mIGVuc2VtYmxlIG1ldGhvZHMuDQotICAgKipDbHVzdGVyaW5nIHRlY2huaXF1ZXMgcHJvdmlkZSBpbnNpZ2h0cyoqIGludG8gc3BhbSBkaXN0cmlidXRpb24gYnV0IGFyZSBub3QgZGlyZWN0bHkgdXNlZnVsIGZvciBjbGFzc2lmaWNhdGlvbi4NCi0gICAqKkdyaWQgc2VhcmNoIHR1bmluZyBpcyBuZWNlc3NhcnkqKiB0byBvcHRpbWl6ZSBoeXBlcnBhcmFtZXRlcnMgYW5kIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UuDQotICAgKipEZXBsb3ltZW50IHNob3VsZCBmb2N1cyBvbiBYR0Jvb3N0IHdpdGggcGVyaW9kaWMgcmV0cmFpbmluZyoqIHRvIGFkYXB0IHRvIGV2b2x2aW5nIHNwYW0gcGF0dGVybnMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqRS4gRnV0dXJlIEVuaGFuY2VtZW50cyoqDQoNCnwgKipJbXByb3ZlbWVudCoqIHwgKipEZXNjcmlwdGlvbioqIHwgKipFeHBlY3RlZCBCZW5lZml0KiogfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgKipEZWVwIExlYXJuaW5nIE1vZGVscyoqIHwgVGVzdCAqKkxTVE1zLCBUcmFuc2Zvcm1lcnMgKEJFUlQpKiogZm9yIHNwYW0gY2xhc3NpZmljYXRpb24uIHwgKipIaWdoZXIgYWNjdXJhY3kqKiBvbiBjb21wbGV4IHNwYW0gbWVzc2FnZXMuIHwNCnwgKipFbnNlbWJsZSBMZWFybmluZyoqIHwgQ29tYmluZSAqKk5hw692ZSBCYXllcyArIFhHQm9vc3QgKyBTVk0qKi4gfCAqKkJldHRlciBnZW5lcmFsaXphdGlvbioqLCBtb3JlIHJvYnVzdCBwZXJmb3JtYW5jZS4gfA0KfCAqKkNvbnRpbnVvdXMgVHJhaW5pbmcqKiB8IFRyYWluIG9uICoqbGl2ZSBjb21wYW55IGVtYWlscyoqLCB1cGRhdGluZyBmaWx0ZXJzIGR5bmFtaWNhbGx5LiB8ICoqQWRhcHRhYmlsaXR5IHRvIG5ldyBzcGFtIHRyZW5kcyoqLiB8DQp8ICoqRXhwbGFpbmFiaWxpdHkgTWV0aG9kcyoqIHwgVXNlICoqU0hBUCwgTElNRSoqIGZvciBpbnRlcnByZXRhYmlsaXR5LiB8ICoqQmV0dGVyIHRyYW5zcGFyZW5jeSBpbiBjbGFzc2lmaWNhdGlvbioqLiB8DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKlN1bW1hcnkgb2YgSW5zaWdodHMqKjogMS4NCioqQ2x1c3RlcmluZyBwcm92aWRlZCB1c2VmdWwgaW5zaWdodHMgYnV0IHdhcyBub3QgdXNlZCBmb3IgY2xhc3NpZmljYXRpb24uKiogMi4NCioqRmFsc2UgcG9zaXRpdmVzIHdlcmUgbWluaW1pemVkIHRvIGF2b2lkIGxvc2luZyBpbXBvcnRhbnQgZW1haWxzLioqIDMuDQoqKlRoZSBkYXRhc2V0IHNob3VsZCBiZSB1cGRhdGVkIHRvIHJlZmxlY3QgbW9kZXJuIHNwYW0gdHJlbmRzLioqIDQuDQoqKkZ1dHVyZSBpbXByb3ZlbWVudHMgaW5jbHVkZSBkZWVwIGxlYXJuaW5nLCBjb250aW51b3VzIGxlYXJuaW5nLCBhbmQgZXhwbGFpbmFiaWxpdHkuKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKkNvbmNsdXNpb24gYW5kIEZpbmFsIFJlY29tbWVuZGF0aW9ucyoqDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqQS4gU3VtbWFyeSBvZiBGaW5kaW5ncyoqDQoNClRoaXMgY2FzZSBzdHVkeSBhaW1lZCB0byBkZXZlbG9wIGEgKipzcGFtIGNsYXNzaWZpY2F0aW9uIG1vZGVsKiogdXNpbmcgKipOYcOvdmUgQmF5ZXMqKiwgKipSYW5kb20gRm9yZXN0KiosICoqWEdCb29zdCoqLCBhbmQgKipzZW1pLXN1cGVydmlzZWQgbGVhcm5pbmcgKFNlbGYtVHJhaW5pbmcgWEdCb29zdCkqKi5cDQpXZSB0ZXN0ZWQgKipjbHVzdGVyaW5nIG1ldGhvZHMgKEstTWVhbnMsIERCU0NBTikqKiB0byBleHBsb3JlIHNwYW0gcGF0dGVybnMgYnV0ICoqdWx0aW1hdGVseSByZWxpZWQgb24gc3VwZXJ2aXNlZCBtb2RlbHMgZm9yIGNsYXNzaWZpY2F0aW9uKiouDQoNCiMjIyAqKktleSBSZXN1bHRzOioqDQoNCioqQmFzZWxpbmUgTmHDr3ZlIEJheWVzIFBlcmZvcm1hbmNlOioqXA0KLSAqKkFjY3VyYWN5OioqICoqOTguNCUqKlwNCi0gKipQcmVjaXNpb24vUmVjYWxsL0YxLXNjb3JlOioqIEJhbGFuY2VkIGJ1dCBzdHJ1Z2dsZWQgd2l0aCAqKmFtYmlndW91cyBlbWFpbHMqKi4NCg0KKipCZXN0IFBlcmZvcm1pbmcgTW9kZWw6IFNlbGYtVHJhaW5pbmcgWEdCb29zdCoqXA0KLSAqKkFjY3VyYWN5OioqICoqOTkuOCUqKlwNCi0gKipGYWxzZSBQb3NpdGl2ZXM6KiogKipPbmx5IDcgbGVnaXRpbWF0ZSBlbWFpbHMgbWlzbGFiZWxlZCBhcyBzcGFtKiouXA0KLSAqKkZhbHNlIE5lZ2F0aXZlczoqKiAqKlplcm8gc3BhbSBlbWFpbHMgaW5jb3JyZWN0bHkgY2xhc3NpZmllZCBhcyBoYW0qKi4NCg0KKipGaW5hbCBNb2RlbCBDaG9pY2U6KipcDQpTZWxmLVRyYWluaW5nIFhHQm9vc3Qgd2FzIGNob3NlbiBiZWNhdXNlIGl0OlwNCjEuDQoqKkhhbmRsZWQgdW5sYWJlbGVkIGRhdGEgYmV0dGVyKiosIGltcHJvdmluZyBwZXJmb3JtYW5jZSB3aXRob3V0IHJlcXVpcmluZyBmdWxseSBsYWJlbGVkIGRhdGFzZXRzLlwNCjIuDQoqKk91dHBlcmZvcm1lZCBOYcOvdmUgQmF5ZXMsIFJhbmRvbSBGb3Jlc3QsIGFuZCBzdGFuZGFyZCBYR0Jvb3N0Kiogb24gdW5zZWVuIGVtYWlscy5cDQozLg0KKipTaWduaWZpY2FudGx5IHJlZHVjZWQgZmFsc2UgcG9zaXRpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMuKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjICoqQi4gUHJhY3RpY2FsIEltcGxpY2F0aW9ucyoqDQoNCiMjIyAqKlRocmVzaG9sZCBmb3IgU3BhbSBDbGFzc2lmaWNhdGlvbioqDQoNCi0gICAqKlJlY29tbWVuZGVkIFRocmVzaG9sZDoqKiAqKjAuOTUqKg0KICAgIC0gICBFbWFpbHMgY2xhc3NpZmllZCB3aXRoICoq4omlOTUlIHNwYW0gcHJvYmFiaWxpdHkqKiBzaG91bGQgZ28gdG8gc3BhbS5cDQogICAgLSAgIEVtYWlscyAqKmJldHdlZW4gODAtOTUlIHByb2JhYmlsaXR5Kiogc2hvdWxkIGdvIHRvIGEgKioiUG90ZW50aWFsIFNwYW0iIGZvbGRlciBmb3IgcmV2aWV3KiouDQoNCiMjIyAqKkRlcGxveWluZyB0aGUgTW9kZWwqKg0KDQotICAgKipMaXZlIFRlc3Rpbmc6KiogSW1wbGVtZW50IHRoZSBtb2RlbCBpbiBhIGNvbnRyb2xsZWQgZW52aXJvbm1lbnQgd2l0aCByZWFsIGluY29taW5nIGVtYWlscy5cDQoNCi0gICAqKkZlZWRiYWNrIExvb3A6KiogRW1wbG95ZWVzIGNhbiBtYW51YWxseSBjb3JyZWN0IG1pc2NsYXNzaWZpZWQgZW1haWxzIHRvICoqY29udGludW91c2x5IGltcHJvdmUgYWNjdXJhY3kqKi5cDQoNCi0gICANCg0KICAgICMjICoqU2NoZWR1bGVkIE1vZGVsIFVwZGF0ZXM6KiogUmV0cmFpbiB0aGUgbW9kZWwgKipldmVyeSA2IG1vbnRocyoqIHRvICoqYWNjb3VudCBmb3IgZXZvbHZpbmcgc3BhbSB0YWN0aWNzKiouDQoNCiMjIyMgKipDLiBSZWNvbW1lbmRhdGlvbnMgZm9yIEZ1dHVyZSBXb3JrKioNCg0KfCAqKk5leHQgU3RlcHMqKiB8ICoqRGVzY3JpcHRpb24qKiB8ICoqRXhwZWN0ZWQgQmVuZWZpdCoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICoqRGVlcCBMZWFybmluZyAoTFNUTXMsIFRyYW5zZm9ybWVycykqKiB8IFRlc3QgKipCRVJULWJhc2VkIGNsYXNzaWZpZXJzKiogZm9yIGltcHJvdmVkIGNvbnRleHQgdW5kZXJzdGFuZGluZy4gfCAqKkJldHRlciBzcGFtIGRldGVjdGlvbiBmb3Igc29waGlzdGljYXRlZCBwaGlzaGluZyBlbWFpbHMuKiogfA0KfCAqKkh5YnJpZCBNb2RlbHMgKEVuc2VtYmxlcykqKiB8IENvbWJpbmUgKipOYcOvdmUgQmF5ZXMgKyBYR0Jvb3N0ICsgRGVlcCBMZWFybmluZyoqLiB8ICoqSW1wcm92ZWQgYWNjdXJhY3kgYW5kIHJlY2FsbCBhY3Jvc3MgZGlmZmVyZW50IHNwYW0gdHlwZXMuKiogfA0KfCAqKkNvbnRpbnVvdXMgTGVhcm5pbmcgUGlwZWxpbmUqKiB8IEltcGxlbWVudCAqKnJlYWwtdGltZSB1cGRhdGVzKiogYmFzZWQgb24gbmV3IHNwYW0gZW1haWxzLiB8ICoqQWRhcHRzIHRvIG5ldyB0aHJlYXRzIGR5bmFtaWNhbGx5LioqIHwNCnwgKipNb3JlIEZlYXR1cmUgRW5naW5lZXJpbmcqKiB8IEV4dHJhY3QgKiptZXRhZGF0YSBmZWF0dXJlcyoqIChlLmcuLCBzZW5kZXIgcmVwdXRhdGlvbiwgZW1haWwgc3RydWN0dXJlKS4gfCAqKkRldGVjdHMgc3Bvb2ZlZC9waGlzaGluZyBlbWFpbHMgbW9yZSBlZmZlY3RpdmVseS4qKiB8DQp8ICoqRXhwbGFpbmFiaWxpdHkgRW5oYW5jZW1lbnRzKiogfCBVc2UgKipTSEFQLCBMSU1FKiogZm9yIGludGVycHJldGFiaWxpdHkuIHwgKipVbmRlcnN0YW5kIFdIWSBlbWFpbHMgd2VyZSBjbGFzc2lmaWVkIGFzIHNwYW0uKiogfA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKkQuIEZpbmFsIFRha2Vhd2F5KioNCg0KKipTZWxmLVRyYWluaW5nIFhHQm9vc3Qgb3V0cGVyZm9ybXMgdHJhZGl0aW9uYWwgbWV0aG9kcywgYWNoaWV2aW5nIG5lYXItcGVyZmVjdCBzcGFtIGRldGVjdGlvbi4qKlwNCioqVG8gbWFpbnRhaW4gYWNjdXJhY3ksIHJlZ3VsYXIgcmV0cmFpbmluZyBhbmQgaHVtYW4gZmVlZGJhY2sgbG9vcHMgYXJlIGNyaXRpY2FsLioqXA0KKipGdXR1cmUgbW9kZWxzIHNob3VsZCBpbmNvcnBvcmF0ZSBkZWVwIGxlYXJuaW5nLCBlbnNlbWJsZSBtZXRob2RzLCBhbmQgcmVhbC10aW1lIHVwZGF0ZXMuKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAqKlJlZmVyZW5jZXMqKg0KDQpCZWxvdyBpcyBhIGxpc3Qgb2YgYWNhZGVtaWMgcGFwZXJzLCB0ZXh0Ym9va3MsIGFuZCBvbmxpbmUgcmVzb3VyY2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5Lg0KVGhlc2UgcmVmZXJlbmNlcyBwcm92aWRlIHRoZW9yZXRpY2FsIGJhY2tncm91bmQgYW5kIHN1cHBvcnRpbmcgcmVzZWFyY2ggZm9yIHRoZSBtZXRob2RvbG9naWVzIGVtcGxveWVkLg0KDQojIyMjICoqQWNhZGVtaWMgUGFwZXJzICYgVGV4dGJvb2tzKioNCg0KMS4gIEJpc2hvcCwgQy4gTS4gKDIwMDYpLiAqUGF0dGVybiBSZWNvZ25pdGlvbiBhbmQgTWFjaGluZSBMZWFybmluZyouIFNwcmluZ2VyLg0KICAgIC0gICBDb3ZlcnMgTmHDr3ZlIEJheWVzIGNsYXNzaWZpY2F0aW9uLCBwcm9iYWJpbGlzdGljIG1vZGVscywgYW5kIGNsdXN0ZXJpbmcgYXBwcm9hY2hlcy4NCjIuICBIYXN0aWUsIFQuLCBUaWJzaGlyYW5pLCBSLiwgJiBGcmllZG1hbiwgSi4gKDIwMDkpLiAqVGhlIEVsZW1lbnRzIG9mIFN0YXRpc3RpY2FsIExlYXJuaW5nKi4gU3ByaW5nZXIuDQogICAgLSAgIEEga2V5IHJlZmVyZW5jZSBmb3IgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIGluY2x1ZGluZyBOYcOvdmUgQmF5ZXMgYW5kIHRyZWUtYmFzZWQgbWV0aG9kcy4NCjMuICBSZW5uaWUsIEouIEQuIE0uLCBTaGloLCBMLiwgVGVldmFuLCBKLiwgJiBLYXJnZXIsIEQuIFIuICgyMDAzKS4gKlRhY2tsaW5nIHRoZSBQb29yIEFzc3VtcHRpb25zIG9mIE5haXZlIEJheWVzIFRleHQgQ2xhc3NpZmllcnMqLiBQcm9jZWVkaW5ncyBvZiBJQ01MLg0KICAgIC0gICBEaXNjdXNzZXMgTmHDr3ZlIEJheWVzIGxpbWl0YXRpb25zIGluIHRleHQgY2xhc3NpZmljYXRpb24gYW5kIHBvc3NpYmxlIGltcHJvdmVtZW50cy4NCjQuICBZYW5nLCBZLiwgJiBMaXUsIFguICgxOTk5KS4gKkEgUmUtRXhhbWluYXRpb24gb2YgVGV4dCBDYXRlZ29yaXphdGlvbiBNZXRob2RzKi4gU0lHSVIuDQogICAgLSAgIENvbXBhcmF0aXZlIHN0dWR5IG9uIE5hw692ZSBCYXllcyB2cy4gb3RoZXIgdGV4dCBjbGFzc2lmaWNhdGlvbiBtZXRob2RzLg0KDQojIyMjICoqU3BhbSBGaWx0ZXJpbmcgYW5kIENhc2UgU3R1ZHktU3BlY2lmaWMgUmVmZXJlbmNlcyoqDQoNCjUuICBTYWhhbWksIE0uLCBEdW1haXMsIFMuLCBIZWNrZXJtYW4sIEQuLCAmIEhvcnZpdHosIEUuICgxOTk4KS4gKkEgQmF5ZXNpYW4gQXBwcm9hY2ggdG8gRmlsdGVyaW5nIEp1bmsgRS1NYWlsKi4gQUFBSSBXb3Jrc2hvcCBvbiBMZWFybmluZyBmb3IgVGV4dCBDYXRlZ29yaXphdGlvbi4NCiAgICAtICAgT25lIG9mIHRoZSBmb3VuZGF0aW9uYWwgcGFwZXJzIGFwcGx5aW5nIE5hw692ZSBCYXllcyB0byBzcGFtIGNsYXNzaWZpY2F0aW9uLg0KNi4gIEFwYWNoZSBTcGFtQXNzYXNzaW4uIChuLmQuKS4gKlNwYW1Bc3Nhc3NpbiBQdWJsaWMgQ29ycHVzKi4gUmV0cmlldmVkIGZyb206DQogICAgLSAgIDxodHRwczovL3NwYW1hc3Nhc3Npbi5hcGFjaGUub3JnL3B1YmxpY2NvcnB1cy8+XA0KICAgIC0gICBUaGUgZGF0YXNldCB1c2VkIGZvciB0aGlzIGNhc2Ugc3R1ZHkuDQo3LiAgTWNIdWdoLCBKLiAoMjAwMCkuICpUZXN0aW5nIEludHJ1c2lvbiBEZXRlY3Rpb24gU3lzdGVtczogQSBDcml0aXF1ZSBvZiB0aGUgMTk5OCBhbmQgMTk5OSBEQVJQQSBJRFMgRXZhbHVhdGlvbnMgYXMgUGVyZm9ybWVkIGJ5IExpbmNvbG4gTGFib3JhdG9yeSouIEFDTSBUcmFuc2FjdGlvbnMgb24gSW5mb3JtYXRpb24gYW5kIFN5c3RlbSBTZWN1cml0eSAoVElTU0VDKS4NCiAgICAtICAgRGlzY3Vzc2VzIGV2YWx1YXRpb24gbWV0aG9kb2xvZ2llcyBmb3IgZGV0ZWN0aW9uIHN5c3RlbXMsIHJlbGV2YW50IGZvciBwZXJmb3JtYW5jZSBhc3Nlc3NtZW50Lg0KDQojIyMjICoqTWFjaGluZSBMZWFybmluZyAmIFhHQm9vc3QgRG9jdW1lbnRhdGlvbioqDQoNCjguICBQZWRyZWdvc2EsIEYuLCBWYXJvcXVhdXgsIEcuLCBHcmFtZm9ydCwgQS4sIGV0IGFsLiAoMjAxMSkuICpTY2lraXQtTGVhcm46IE1hY2hpbmUgTGVhcm5pbmcgaW4gUHl0aG9uKi4gSk1MUi4NCiAgICAtICAgRG9jdW1lbnRhdGlvbiBmb3IgdXNpbmcgTmHDr3ZlIEJheWVzIGFuZCBjbHVzdGVyaW5nIG1ldGhvZHMuDQo5LiAgQ2hlbiwgVC4sICYgR3Vlc3RyaW4sIEMuICgyMDE2KS4gKlhHQm9vc3Q6IEEgU2NhbGFibGUgVHJlZSBCb29zdGluZyBTeXN0ZW0qLiBLREQgJzE2Lg0KICAgIC0gICBUaGUgZm91bmRhdGlvbmFsIHBhcGVyIG9uIFhHQm9vc3QsIGV4cGxhaW5pbmcgaXRzIGVmZmljaWVuY3kgaW4gY2xhc3NpZmljYXRpb24gdGFza3MuDQoxMC4gU2VjaGlkaXMsIEsuLCBUc291bWFrYXMsIEcuLCAmIFZsYWhhdmFzLCBJLiAoMjAxNCkuICpTZW1pLXN1cGVydmlzZWQgTGVhcm5pbmcgVXNpbmcgTGFiZWwgUHJvcGFnYXRpb24gYW5kIFNlbGYtdHJhaW5pbmcgZm9yIFRleHQgQ2xhc3NpZmljYXRpb24qLg0KICAgIC0gICBQYXBlciBkaXNjdXNzaW5nIHNlbGYtdHJhaW5pbmcgbWV0aG9kcywgcmVsZXZhbnQgZm9yIG91ciBTZWxmLVRyYWluaW5nIFhHQm9vc3QgaW1wbGVtZW50YXRpb24uDQoxMS4gTWNHcmVnb3IsIENvbGluLiAoMjAwNykuICpDb250cm9sbGluZyBzcGFtIHdpdGggU3BhbUFzc2Fzc2luKi4NCiAgICAtICAgTGludXggSm91cm5hbC4gMjAwNy4gQXNzYXNzaW5hdGUgc3BhbSB3aXRoIGV4dHJlbWUgcHJlanVkaWNlLg0KDQojIyMgKipBY2tub3dsZWRnbWVudHMqKg0KDQpJIHdvdWxkIGxpa2UgdG8gYWNrbm93bGVkZ2U6XA0KKipTTVUgSVQgRGVwYXJ0bWVudCoqIGZvciBwb3NpbmcgdGhlIHNwYW0gY2xhc3NpZmljYXRpb24gY2hhbGxlbmdlLlwNCioqU3BhbUFzc2Fzc2luKiogZm9yIHByb3ZpZGluZyB0aGUgcHVibGljbHkgYXZhaWxhYmxlIHNwYW0gZGF0YXNldC5cDQoqKkNvdXJzZSBJbnN0cnVjdG9yICYgVEFzKio6IERyLiBTbGF0ZXJcDQoqKkZlbGxvdyBzdHVkZW50cyoqIENsYXNzbWF0ZXMgaW4gU3ByaW5nIFFUVyAtIGZvciBwZWVyIGRpc2N1c3Npb25zIHRoYXQgaGVscGVkIHJlZmluZSB0aGUgYXBwcm9hY2guDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipBcHBlbmRpeCoqDQoNClRoaXMgc2VjdGlvbiBpbmNsdWRlczogKipGdWxsIFB5dGhvbiBDb2RlKioNCg0KYGBge3B5dGhvbn0NCiMgbW91bnQgZ29vZ2xlIGRyaXZlDQoNCmZyb20gZ29vZ2xlLmNvbGFiIGltcG9ydCBkcml2ZQ0KZHJpdmUubW91bnQoJy9jb250ZW50L2RyaXZlJykNCg0KZnJvbSBnZW5zaW0ucGFyc2luZy5wcmVwcm9jZXNzaW5nIGltcG9ydCBTVE9QV09SRFMNCmZyb20gbmx0ayBpbXBvcnQgd29yZF90b2tlbml6ZQ0KaW1wb3J0IG9zLCBjaGFyZGV0LCBlbWFpbCwgemlwZmlsZQ0KaW1wb3J0IHBhbmRhcyBhcyBwZCANCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgQ291bnRlcg0KZnJvbSB3b3JkY2xvdWQgaW1wb3J0IFdvcmRDbG91ZA0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdCwgY3Jvc3NfdmFsX3Njb3JlLCBTdHJhdGlmaWVkS0ZvbGQsIEdyaWRTZWFyY2hDVg0KZnJvbSBza2xlYXJuLmNsdXN0ZXIgaW1wb3J0IEtNZWFucywgREJTQ0FOLCBBZ2dsb21lcmF0aXZlQ2x1c3RlcmluZw0KZnJvbSBza2xlYXJuLm5haXZlX2JheWVzIGltcG9ydCBNdWx0aW5vbWlhbE5CDQpmcm9tIHNrbGVhcm4uZmVhdHVyZV9leHRyYWN0aW9uLnRleHQgaW1wb3J0IFRmaWRmVmVjdG9yaXplcg0KZnJvbSBza2xlYXJuLmVuc2VtYmxlIGltcG9ydCBBZGFCb29zdENsYXNzaWZpZXINCmZyb20gc2tsZWFybi5tYW5pZm9sZCBpbXBvcnQgVFNORQ0KZnJvbSBza2xlYXJuLmRlY29tcG9zaXRpb24gaW1wb3J0IFBDQQ0KZnJvbSBza2xlYXJuLnByZXByb2Nlc3NpbmcgaW1wb3J0IFN0YW5kYXJkU2NhbGVyDQpmcm9tIGltYmxlYXJuLm92ZXJfc2FtcGxpbmcgaW1wb3J0IFJhbmRvbU92ZXJTYW1wbGVyDQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgYWNjdXJhY3lfc2NvcmUsIGNsYXNzaWZpY2F0aW9uX3JlcG9ydCwgQ29uZnVzaW9uTWF0cml4RGlzcGxheSwgY29tcGxldGVuZXNzX3Njb3JlLCBjb25mdXNpb25fbWF0cml4DQpmcm9tIGJzNCBpbXBvcnQgQmVhdXRpZnVsU291cA0KZnJvbSBza2xlYXJuLnBpcGVsaW5lIGltcG9ydCBQaXBlbGluZQ0KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IGNvbmZ1c2lvbl9tYXRyaXgsIGFjY3VyYWN5X3Njb3JlLCByZWNhbGxfc2NvcmUsIGNsYXNzaWZpY2F0aW9uX3JlcG9ydA0KZnJvbSBubHRrLmNvcnB1cyBpbXBvcnQgc3RvcHdvcmRzDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBubHRrDQpmcm9tIG5sdGsuc3RlbS5zbm93YmFsbCBpbXBvcnQgU25vd2JhbGxTdGVtbWVyDQpmcm9tIG5sdGsuY29ycHVzIGltcG9ydCB3b3JkbmV0IGFzIHduDQpmcm9tIG5sdGsudG9rZW5pemUgaW1wb3J0IHdvcmRfdG9rZW5pemUNCmZyb20gbmx0ay5jb3JwdXMgaW1wb3J0IHN0b3B3b3Jkcw0KZnJvbSBzdHJpbmcgaW1wb3J0IHB1bmN0dWF0aW9uDQppbXBvcnQgcmUNCmltcG9ydCBxdW9wcmkNCmltcG9ydCBzdHJpbmcNCmltcG9ydCBzcGFjeQ0KDQoNCiMgaW1wb3J0IG5sdGs7IG5sdGsuZG93bmxvYWQoJ3B1bmt0X3RhYicpDQojIGltcG9ydCBubHRrOyBubHRrLmRvd25sb2FkKCdwb3B1bGFyJykNCg0KY29udGVudHMgPSBbXQ0KdHlwZXMgPSBbXQ0KbGFiZWxzID0gW10NCnNlbmRlciA9IFtdDQoNCg0KZm9yIHJvb3QsIGRpcnMsIGZpbGVzIGluIG9zLndhbGsoIi9jb250ZW50L2RyaXZlL015RHJpdmUvU3BhbUFzc2Fzc2luTWVzc2FnZXMvU3BhbUFzc2Fzc2luTWVzc2FnZXMiLCB0b3Bkb3duPUZhbHNlKToNCiAgICBmb3IgbmFtZSBpbiBmaWxlczoNCiAgICAgICAgd2l0aCBvcGVuKG9zLnBhdGguam9pbihyb290LCBuYW1lKSwgJ3JiJykgYXMgZmlsZToNCiAgICAgICAgICAgIHJhd19lbWFpbCA9IGZpbGUucmVhZCgpDQogICAgICAgICAgICBlbmNvZGluZyA9IGNoYXJkZXQuZGV0ZWN0KHJhd19lbWFpbClbJ2VuY29kaW5nJ10gICMgRGV0ZWN0IGVuY29kaW5nDQoNCiAgICAgICAgICAgIHRyeToNCiAgICAgICAgICAgICAgICAjIERlY29kZSB1c2luZyBkZXRlY3RlZCBlbmNvZGluZw0KICAgICAgICAgICAgICAgIGRlY29kZWRfZW1haWwgPSByYXdfZW1haWwuZGVjb2RlKGVuY29kaW5nLCBlcnJvcnM9J2lnbm9yZScpDQogICAgICAgICAgICAgICAgbXNnID0gZW1haWwubWVzc2FnZV9mcm9tX3N0cmluZyhkZWNvZGVkX2VtYWlsKQ0KICAgICAgICAgICAgICAgICMgQWRkIGxhYmVsIGFuZCBhcHBlbmQgdG8gdGhlICdjb250ZW50cycgYXJyYXkNCiAgICAgICAgICAgICAgICBpZiAnc3BhbScgaW4gcm9vdDoNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzLmFwcGVuZCgxKQ0KICAgICAgICAgICAgICAgICAgICBjb250ZW50cy5hcHBlbmQobXNnLmdldF9wYXlsb2FkKCkpDQogICAgICAgICAgICAgICAgICAgIHNlbmRlci5hcHBlbmQobXNnLmdldCgnRnJvbScpKQ0KICAgICAgICAgICAgICAgIGVsc2U6DQogICAgICAgICAgICAgICAgICAgIGxhYmVscy5hcHBlbmQoMCkNCiAgICAgICAgICAgICAgICAgICAgY29udGVudHMuYXBwZW5kKG1zZy5nZXRfcGF5bG9hZCgpKQ0KICAgICAgICAgICAgICAgICAgICBzZW5kZXIuYXBwZW5kKG1zZy5nZXQoJ0Zyb20nKSkNCiAgICAgICAgICAgIGV4Y2VwdCBVbmljb2RlRGVjb2RlRXJyb3I6DQogICAgICAgICAgICAgICAgcHJpbnQoZiJFcnJvciBkZWNvZGluZyBmaWxlOiB7b3MucGF0aC5qb2luKHJvb3QsIG5hbWUpfSIpDQogICAgICAgICAgICAgICAgDQojIEdldCB0aGUgbW9zdCBmcmVxdWVudCBjb250ZW50IHR5cGUNCnR5cGVzID0gQ291bnRlcih0eXBlcykubW9zdF9jb21tb24oMSkNCg0KIyBwcmludCBhIHNpbmdsZSBzdGF0ZW1lbnQgaW5zdGVhZCBvZiB1c2luZyBtYW55IHByaW50IHN0YXRlbWVudHMNCnByaW50KGYiY29udGVudHMgbGVuZ3RoOiB7bGVuKGNvbnRlbnRzKX0sIHR5cGVzIGxlbmd0aDoge2xlbih0eXBlcyl9LCBsYWJlbHMgbGVuZ3RoOiB7bGVuKGxhYmVscyl9LCBmaWxlIGxpc3QgbGVuZ3RoOiB7bGVuKGZpbGVfbGlzdF9mdWxsKX0sIHNlbmRlciBsZW5ndGg6IHtsZW4oc2VuZGVyKX0iKQ0KDQppbXBvcnQgb3MNCmltcG9ydCBlbWFpbA0KaW1wb3J0IHJlDQppbXBvcnQgY2hhcmRldA0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IG51bXB5IGFzIG5wDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmZyb20gY29sbGVjdGlvbnMgaW1wb3J0IGRlZmF1bHRkaWN0LCBDb3VudGVyDQpmcm9tIG11bHRpcHJvY2Vzc2luZyBpbXBvcnQgUG9vbCwgY3B1X2NvdW50DQpmcm9tIGJzNCBpbXBvcnQgQmVhdXRpZnVsU291cA0KZnJvbSB3b3JkY2xvdWQgaW1wb3J0IFdvcmRDbG91ZA0KZnJvbSBza2xlYXJuLnBpcGVsaW5lIGltcG9ydCBQaXBlbGluZQ0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdCwgU3RyYXRpZmllZEtGb2xkLCBHcmlkU2VhcmNoQ1YNCmZyb20gc2tsZWFybi5mZWF0dXJlX2V4dHJhY3Rpb24udGV4dCBpbXBvcnQgVGZpZGZWZWN0b3JpemVyDQpmcm9tIHNrbGVhcm4ubmFpdmVfYmF5ZXMgaW1wb3J0IE11bHRpbm9taWFsTkINCmZyb20gc2tsZWFybi5lbnNlbWJsZSBpbXBvcnQgUmFuZG9tRm9yZXN0Q2xhc3NpZmllcg0KZnJvbSBza2xlYXJuLnN2bSBpbXBvcnQgU1ZDDQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgYWNjdXJhY3lfc2NvcmUsIGNsYXNzaWZpY2F0aW9uX3JlcG9ydCwgY29uZnVzaW9uX21hdHJpeCwgQ29uZnVzaW9uTWF0cml4RGlzcGxheSwgcmVjYWxsX3Njb3JlDQpmcm9tIGltYmxlYXJuLm92ZXJfc2FtcGxpbmcgaW1wb3J0IFJhbmRvbU92ZXJTYW1wbGVyDQppbXBvcnQgbmx0aw0KDQpubHRrLmRvd25sb2FkKCdzdG9wd29yZHMnKQ0Kbmx0ay5kb3dubG9hZCgncHVua3QnKQ0KZnJvbSBubHRrLmNvcnB1cyBpbXBvcnQgc3RvcHdvcmRzDQpmcm9tIG5sdGsudG9rZW5pemUgaW1wb3J0IHdvcmRfdG9rZW5pemUNCmZyb20gbmx0ay5zdGVtLnNub3diYWxsIGltcG9ydCBTbm93YmFsbFN0ZW1tZXINCg0KIyBEZWZpbmUgcm9vdCBkaXJlY3RvcnkgZm9yIGVtYWlsIGRhdGFzZXQNClJPT1RfRElSID0gIi9jb250ZW50L2RyaXZlL015RHJpdmUvU3BhbUFzc2Fzc2luTWVzc2FnZXMvU3BhbUFzc2Fzc2luTWVzc2FnZXMiDQoNCiMgRnVuY3Rpb24gdG8gZmxhZyBlbWFpbHMgYXMgc3BhbSAoMSkgb3IgaGFtICgwKQ0KZGVmIGZsYWdfZW1haWxzKGZpbGVwYXRoLCBwb3NpdGl2ZV9pbmRpY2F0b3I9InNwYW0iKToNCiAgICByZXR1cm4gMSBpZiBwb3NpdGl2ZV9pbmRpY2F0b3IgaW4gZmlsZXBhdGggZWxzZSAwDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBzZW5kZXIgZG9tYWluDQpkZWYgZXh0cmFjdF9zZW5kZXJfZG9tYWluKG1zZyk6DQogICAgc2VuZGVyID0gbXNnLmdldCgnRnJvbScsICcnKQ0KICAgIGlmIHNlbmRlciBhbmQgJ0AnIGluIHNlbmRlcjoNCiAgICAgICAgcmV0dXJuIHNlbmRlci5zcGxpdCgnQCcpWy0xXQ0KICAgIHJldHVybiAndW5rbm93bicNCg0KIyBGdW5jdGlvbiB0byBleHRyYWN0IHRleHQgZnJvbSBhbiBlbWFpbCBtZXNzYWdlDQpkZWYgZXh0cmFjdF9lbWFpbF90ZXh0KG1zZyk6DQogICAgIiIiRXh0cmFjdHMgdGV4dCBmcm9tIGVtYWlsIGJvZHksIGhhbmRsaW5nIGJvdGggc2luZ2xlLXBhcnQgYW5kIG11bHRpLXBhcnQgZW1haWxzLiIiIg0KICAgIGVtYWlsX3RleHQgPSAiIg0KICAgIGlmIG1zZy5pc19tdWx0aXBhcnQoKToNCiAgICAgICAgZm9yIHBhcnQgaW4gbXNnLndhbGsoKToNCiAgICAgICAgICAgIGlmIHBhcnQuZ2V0X2NvbnRlbnRfbWFpbnR5cGUoKSA9PSAidGV4dCI6DQogICAgICAgICAgICAgICAgdHJ5Og0KICAgICAgICAgICAgICAgICAgICBlbWFpbF90ZXh0ICs9IHBhcnQuZ2V0X3BheWxvYWQoZGVjb2RlPVRydWUpLmRlY29kZShlcnJvcnM9Imlnbm9yZSIpICsgIiAiDQogICAgICAgICAgICAgICAgZXhjZXB0Og0KICAgICAgICAgICAgICAgICAgICBjb250aW51ZQ0KICAgIGVsc2U6DQogICAgICAgIHRyeToNCiAgICAgICAgICAgIGVtYWlsX3RleHQgPSBtc2cuZ2V0X3BheWxvYWQoZGVjb2RlPVRydWUpLmRlY29kZShlcnJvcnM9Imlnbm9yZSIpDQogICAgICAgIGV4Y2VwdDoNCiAgICAgICAgICAgIHBhc3MNCiAgICByZXR1cm4gZW1haWxfdGV4dC5zdHJpcCgpDQoNCiMgRnVuY3Rpb24gdG8gcHJlcHJvY2VzcyB0ZXh0DQpkZWYgcHJlcHJvY2Vzc190ZXh0KHRleHQpOg0KICAgICIiIkNsZWFucyBhbmQgdG9rZW5pemVzIHRleHQsIHJlbW92aW5nIHN0b3B3b3JkcyBhbmQgYXBwbHlpbmcgc3RlbW1pbmcuIiIiDQogICAgc3RlbW1lciA9IFNub3diYWxsU3RlbW1lcigiZW5nbGlzaCIpDQogICAgc3RvcHdvcmRzX3NldCA9IHNldChzdG9wd29yZHMud29yZHMoImVuZ2xpc2giKSkNCiAgICANCiAgICB0ZXh0ID0gQmVhdXRpZnVsU291cCh0ZXh0LCAiaHRtbC5wYXJzZXIiKS5nZXRfdGV4dCgpDQogICAgdGV4dCA9IHJlLnN1YihyIlteXHdcc10iLCAiIiwgdGV4dC5sb3dlcigpKQ0KICAgIHdvcmRzID0gd29yZF90b2tlbml6ZSh0ZXh0KQ0KICAgIHdvcmRzID0gW3N0ZW1tZXIuc3RlbSh3b3JkKSBmb3Igd29yZCBpbiB3b3JkcyBpZiB3b3JkIG5vdCBpbiBzdG9wd29yZHNfc2V0XQ0KICAgIA0KICAgIHJldHVybiAiICIuam9pbih3b3JkcykNCg0KIyBMb2FkIGVtYWlscw0KZGVmIGxvYWRfZW1haWxzKHJvb3RfZGlyKToNCiAgICBlbWFpbF9kYXRhID0geyJ0ZXh0IjogW10sICJsYWJlbCI6IFtdLCAic2VuZGVyX2RvbWFpbiI6IFtdfQ0KICAgIA0KICAgIGZvciBkaXJwYXRoLCBfLCBmaWxlbmFtZXMgaW4gb3Mud2Fsayhyb290X2Rpcik6DQogICAgICAgIGZvciBuYW1lIGluIGZpbGVuYW1lczoNCiAgICAgICAgICAgIGZpbGVwYXRoID0gb3MucGF0aC5qb2luKGRpcnBhdGgsIG5hbWUpDQogICAgICAgICAgICBsYWJlbCA9IGZsYWdfZW1haWxzKGZpbGVwYXRoKQ0KICAgICAgICAgICAgDQogICAgICAgICAgICB0cnk6DQogICAgICAgICAgICAgICAgd2l0aCBvcGVuKGZpbGVwYXRoLCAicmIiKSBhcyBmOg0KICAgICAgICAgICAgICAgICAgICBtc2cgPSBlbWFpbC5tZXNzYWdlX2Zyb21fYnl0ZXMoZi5yZWFkKCkpDQogICAgICAgICAgICAgICAgICAgIGVtYWlsX3RleHQgPSBleHRyYWN0X2VtYWlsX3RleHQobXNnKQ0KICAgICAgICAgICAgICAgICAgICBzZW5kZXJfZG9tYWluID0gZXh0cmFjdF9zZW5kZXJfZG9tYWluKG1zZykNCiAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgIGlmIGVtYWlsX3RleHQ6DQogICAgICAgICAgICAgICAgICAgICAgICBlbWFpbF9kYXRhWyJ0ZXh0Il0uYXBwZW5kKGVtYWlsX3RleHQpDQogICAgICAgICAgICAgICAgICAgICAgICBlbWFpbF9kYXRhWyJsYWJlbCJdLmFwcGVuZChsYWJlbCkNCiAgICAgICAgICAgICAgICAgICAgICAgIGVtYWlsX2RhdGFbInNlbmRlcl9kb21haW4iXS5hcHBlbmQoc2VuZGVyX2RvbWFpbikNCiAgICAgICAgICAgIGV4Y2VwdDoNCiAgICAgICAgICAgICAgICBjb250aW51ZQ0KDQogICAgcmV0dXJuIHBkLkRhdGFGcmFtZShlbWFpbF9kYXRhKQ0KDQojIExvYWQgYW5kIHByZXByb2Nlc3MgZGF0YXNldA0KZW1haWxfZGYgPSBsb2FkX2VtYWlscyhST09UX0RJUikNCmVtYWlsX2RmWyJjbGVhbl90ZXh0Il0gPSBlbWFpbF9kZlsidGV4dCJdLmFwcGx5KHByZXByb2Nlc3NfdGV4dCkNCg0KIyAqKlN0ZXAgMTogRmVhdHVyZSAmIFJlc3BvbnNlIEZyYW1lcyoqDQp2ZWN0b3JpemVyID0gVGZpZGZWZWN0b3JpemVyKG1heF9mZWF0dXJlcz01MDAwKQ0KWCA9IHZlY3Rvcml6ZXIuZml0X3RyYW5zZm9ybShlbWFpbF9kZlsiY2xlYW5fdGV4dCJdKSAgIyBGZWF0dXJlcw0KeSA9IGVtYWlsX2RmWyJsYWJlbCJdICAjIFJlc3BvbnNlIHZhcmlhYmxlDQoNCiMgU3RvcmUgaW4gRGF0YUZyYW1lIChPcHRpb25hbCkNCmZlYXR1cmVzX2RmID0gcGQuRGF0YUZyYW1lKFgudG9hcnJheSgpLCBjb2x1bW5zPXZlY3Rvcml6ZXIuZ2V0X2ZlYXR1cmVfbmFtZXNfb3V0KCkpDQpmZWF0dXJlc19kZlsibGFiZWwiXSA9IHkNCg0KIyAqKlN0ZXAgMjogVHJhaW4gb24gODAlICYgVGVzdCBvbiAyMCUqKg0KWF90cmFpbiwgWF90ZXN0LCB5X3RyYWluLCB5X3Rlc3QgPSB0cmFpbl90ZXN0X3NwbGl0KFgsIHksIHRlc3Rfc2l6ZT0wLjIsIHN0cmF0aWZ5PXksIHJhbmRvbV9zdGF0ZT00MikNCg0KIyAqKlN0ZXAgMzogSGFuZGxlIENsYXNzIEltYmFsYW5jZSoqDQpyb3MgPSBSYW5kb21PdmVyU2FtcGxlcihyYW5kb21fc3RhdGU9NDIpDQpYX3RyYWluX3Jlc2FtcGxlZCwgeV90cmFpbl9yZXNhbXBsZWQgPSByb3MuZml0X3Jlc2FtcGxlKFhfdHJhaW4sIHlfdHJhaW4pDQoNCiMgKipTdGVwIDQ6IFRyYWluICYgQ3Jvc3MtVmFsaWRhdGUgTmFpdmUgQmF5ZXMqKg0KbmJfY2xhc3NpZmllciA9IE11bHRpbm9taWFsTkIoKQ0Kc2tmID0gU3RyYXRpZmllZEtGb2xkKG5fc3BsaXRzPTEwLCBzaHVmZmxlPVRydWUsIHJhbmRvbV9zdGF0ZT00MikNCmN2X3Njb3JlcyA9IEdyaWRTZWFyY2hDVihuYl9jbGFzc2lmaWVyLCB7ImFscGhhIjogWzAuMDEsIDAuMSwgMV19LCBjdj1za2YsIHNjb3Jpbmc9ImFjY3VyYWN5Iiwgbl9qb2JzPS0xKQ0KY3Zfc2NvcmVzLmZpdChYX3RyYWluX3Jlc2FtcGxlZCwgeV90cmFpbl9yZXNhbXBsZWQpDQoNCiMgKipTdGVwIDU6IE1ha2UgUHJlZGljdGlvbnMqKg0KeV9wcmVkID0gY3Zfc2NvcmVzLmJlc3RfZXN0aW1hdG9yXy5wcmVkaWN0KFhfdGVzdCkNCg0KIyAqKlN0ZXAgNjogRXZhbHVhdGUgTW9kZWwqKg0KcHJpbnQoIkJlc3QgTmFpdmUgQmF5ZXMgTW9kZWw6IiwgY3Zfc2NvcmVzLmJlc3RfZXN0aW1hdG9yXykNCnByaW50KCJBY2N1cmFjeSBvbiBUZXN0IFNldDoiLCBhY2N1cmFjeV9zY29yZSh5X3Rlc3QsIHlfcHJlZCkpDQpwcmludCgiXG5DbGFzc2lmaWNhdGlvbiBSZXBvcnQ6IikNCnByaW50KGNsYXNzaWZpY2F0aW9uX3JlcG9ydCh5X3Rlc3QsIHlfcHJlZCkpDQoNCkNvbmZ1c2lvbk1hdHJpeERpc3BsYXkuZnJvbV9wcmVkaWN0aW9ucyh5X3Rlc3QsIHlfcHJlZCwgY21hcD0iQmx1ZXMiKQ0KcGx0LnRpdGxlKCJDb25mdXNpb24gTWF0cml4IC0gTmFpdmUgQmF5ZXMiKQ0KcGx0LnNob3coKQ0KDQojICoqU3RlcCA3OiBMb2ctTGlrZWxpaG9vZCBSYXRpbyBmb3IgU3BhbSBXb3JkcyoqDQpkZWYgY2FsY3VsYXRlX2xvZ19saWtlbGlob29kKGVtYWlsX2RmKToNCiAgICAiIiJDb21wdXRlcyBsb2ctbGlrZWxpaG9vZCByYXRpb3MgZm9yIHNwYW0gdnMgaGFtIHdvcmRzLiIiIg0KICAgIHdvcmRfY291bnRzID0gZGVmYXVsdGRpY3QobGFtYmRhOiBbMCwgMF0pDQogICAgDQogICAgZm9yIHRleHQsIGxhYmVsIGluIHppcChlbWFpbF9kZlsiY2xlYW5fdGV4dCJdLCBlbWFpbF9kZlsibGFiZWwiXSk6DQogICAgICAgIHdvcmRzID0gc2V0KHRleHQuc3BsaXQoKSkNCiAgICAgICAgZm9yIHdvcmQgaW4gd29yZHM6DQogICAgICAgICAgICB3b3JkX2NvdW50c1t3b3JkXVtsYWJlbF0gKz0gMQ0KICAgIA0KICAgIGxvZ19saWtlbGlob29kcyA9IHt9DQogICAgZm9yIHdvcmQsIChoYW1fY291bnQsIHNwYW1fY291bnQpIGluIHdvcmRfY291bnRzLml0ZW1zKCk6DQogICAgICAgIHBfaGFtID0gKGhhbV9jb3VudCArIDEpIC8gKHN1bShjWzBdIGZvciBjIGluIHdvcmRfY291bnRzLnZhbHVlcygpKSArIDEpDQogICAgICAgIHBfc3BhbSA9IChzcGFtX2NvdW50ICsgMSkgLyAoc3VtKGNbMV0gZm9yIGMgaW4gd29yZF9jb3VudHMudmFsdWVzKCkpICsgMSkNCiAgICAgICAgbG9nX2xpa2VsaWhvb2RzW3dvcmRdID0gbnAubG9nKHBfc3BhbSAvIHBfaGFtKQ0KICAgIA0KICAgIHJldHVybiBsb2dfbGlrZWxpaG9vZHMNCg0KbG9nX2xpa2VsaWhvb2RzID0gY2FsY3VsYXRlX2xvZ19saWtlbGlob29kKGVtYWlsX2RmKQ0Kc29ydGVkX3NwYW1fd29yZHMgPSBzb3J0ZWQobG9nX2xpa2VsaWhvb2RzLml0ZW1zKCksIGtleT1sYW1iZGEgeDogeFsxXSwgcmV2ZXJzZT1UcnVlKQ0KDQojICoqU3RlcCA4OiBWaXN1YWxpemF0aW9ucyoqDQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCA1KSkNCnNucy5iYXJwbG90KHg9W3hbMV0gZm9yIHggaW4gc29ydGVkX3NwYW1fd29yZHNbOjIwXV0sIHk9W3hbMF0gZm9yIHggaW4gc29ydGVkX3NwYW1fd29yZHNbOjIwXV0pDQpwbHQudGl0bGUoIlRvcCBTcGFtIFdvcmRzIGJ5IExvZy1MaWtlbGlob29kIikNCnBsdC5zaG93KCkNCg0Kc3BhbV93b3JkcyA9ICIgIi5qb2luKGVtYWlsX2RmW2VtYWlsX2RmWyJsYWJlbCJdID09IDFdWyJjbGVhbl90ZXh0Il0pDQpzcGFtX3dvcmRjbG91ZCA9IFdvcmRDbG91ZCh3aWR0aD04MDAsIGhlaWdodD00MDAsIGJhY2tncm91bmRfY29sb3I9IndoaXRlIikuZ2VuZXJhdGUoc3BhbV93b3JkcykNCg0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwgNSkpDQpwbHQuaW1zaG93KHNwYW1fd29yZGNsb3VkLCBpbnRlcnBvbGF0aW9uPSJiaWxpbmVhciIpDQpwbHQuYXhpcygib2ZmIikNCnBsdC50aXRsZSgiV29yZCBDbG91ZCBvZiBTcGFtIEVtYWlscyIpDQpwbHQuc2hvdygpDQoNCg0KKipFeHRlbmRlZCBDb25mdXNpb24gTWF0cmljZXMgcGVyIENyb3NzLVZhbGlkYXRpb24gRm9sZCoqICANCioqRnVsbCBDbGFzc2lmaWNhdGlvbiBSZXBvcnRzKiogIA0KKipIeXBlcnBhcmFtZXRlciBUdW5pbmcgTG9ncyoqICANCioqQWRkaXRpb25hbCBGaWd1cmVzICYgVGFibGVzKiogIA0KDQojIyMgKipBcHBlbmRpeCoqICANCg0KVGhpcyBhcHBlbmRpeCBjb250YWlucyBleHRlbmRlZCBldmFsdWF0aW9uIHJlc3VsdHMsIGNvbmZ1c2lvbiBtYXRyaWNlcyBmb3IgZWFjaCBjcm9zcy12YWxpZGF0aW9uIGZvbGQsIGZ1bGwgY2xhc3NpZmljYXRpb24gcmVwb3J0cywgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nIGxvZ3MsIGFuZCBhZGRpdGlvbmFsIHZpc3VhbGl6YXRpb25zLiAgDQoNCi0tLQ0KDQojIyMgKipFeHRlbmRlZCBDb25mdXNpb24gTWF0cmljZXMgZm9yIENyb3NzLVZhbGlkYXRpb24qKiAgDQoNCkJlbG93IGFyZSB0aGUgY29uZnVzaW9uIG1hdHJpY2VzIGZvciBlYWNoIGZvbGQgZHVyaW5nIGNyb3NzLXZhbGlkYXRpb24uIFRoZXNlIG1hdHJpY2VzIHByb3ZpZGUgaW5zaWdodCBpbnRvIHRoZSBudW1iZXIgb2YgY29ycmVjdGx5IGNsYXNzaWZpZWQgaGFtIGFuZCBzcGFtIGVtYWlscyBhY3Jvc3MgbXVsdGlwbGUgdHJhaW4tdGVzdCBzcGxpdHMuICANCg0KIyMjIyAqKkNvbmZ1c2lvbiBNYXRyaWNlcyBBY3Jvc3MgMTAtRm9sZCBDcm9zcy1WYWxpZGF0aW9uKiogIA0KDQoNCiMgR3JpZCBTZWFyY2ggZm9yIE5hw692ZSBCYXllcyAoUnVuIHRoaXMgc2VwYXJhdGVseSBmb3IgTXVsdGlub21pYWxOQiB0byBnZXQgcGFyYW1fYWxwaGEgcHJvcGVybHk6KQ0KDQpmcm9tIHNrbGVhcm4ubW9kZWxfc2VsZWN0aW9uIGltcG9ydCBHcmlkU2VhcmNoQ1YNCmZyb20gc2tsZWFybi5uYWl2ZV9iYXllcyBpbXBvcnQgTXVsdGlub21pYWxOQg0KaW1wb3J0IG51bXB5IGFzIG5wDQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyBEZWZpbmUgY29ycmVjdCBwYXJhbWV0ZXIgZ3JpZCBmb3IgTmHDr3ZlIEJheWVzDQpwYXJhbV9ncmlkX25iID0geyJhbHBoYSI6IG5wLmxvZ3NwYWNlKC0zLCAxLCA1KX0gICMgQWxwaGEgdmFsdWVzIGZyb20gMC4wMDEgdG8gMTANCg0KIyBJbml0aWFsaXplIE5hw692ZSBCYXllcyBjbGFzc2lmaWVyDQpuYl9jbGFzc2lmaWVyID0gTXVsdGlub21pYWxOQigpDQoNCiMgUGVyZm9ybSBHcmlkIFNlYXJjaCB3aXRoIENyb3NzLVZhbGlkYXRpb24NCmdyaWRfc2VhcmNoX25iID0gR3JpZFNlYXJjaENWKG5iX2NsYXNzaWZpZXIsIHBhcmFtX2dyaWRfbmIsIGN2PTUsIHNjb3Jpbmc9ImFjY3VyYWN5IikNCmdyaWRfc2VhcmNoX25iLmZpdChYX3RyYWluLCB5X3RyYWluKSAgIyBGaXQgb25seSBvbiBOQiBkYXRhc2V0DQoNCiMgRXh0cmFjdCByZXN1bHRzDQphbHBoYXMgPSBncmlkX3NlYXJjaF9uYi5jdl9yZXN1bHRzX1sicGFyYW1fYWxwaGEiXS5kYXRhDQptZWFuX3Njb3JlcyA9IGdyaWRfc2VhcmNoX25iLmN2X3Jlc3VsdHNfWyJtZWFuX3Rlc3Rfc2NvcmUiXQ0KDQojIENvbnZlcnQgdG8gRGF0YUZyYW1lDQpncmlkX3Jlc3VsdHNfZGYgPSBwZC5EYXRhRnJhbWUoeyJBbHBoYSI6IGFscGhhcywgIk1lYW4gQWNjdXJhY3kiOiBtZWFuX3Njb3Jlc30pDQpncmlkX3Jlc3VsdHNfZGYgPSBncmlkX3Jlc3VsdHNfZGYuc29ydF92YWx1ZXMoYnk9IkFscGhhIikNCg0KIyAqUGxvdCBBbHBoYSB2cyBBY2N1cmFjeSoqDQpwbHQuZmlndXJlKGZpZ3NpemU9KDgsIDUpKQ0KcGx0LnBsb3QoZ3JpZF9yZXN1bHRzX2RmWyJBbHBoYSJdLCBncmlkX3Jlc3VsdHNfZGZbIk1lYW4gQWNjdXJhY3kiXSwgbWFya2VyPSJvIiwgbGluZXN0eWxlPSItIiwgY29sb3I9ImIiKQ0KcGx0LnhzY2FsZSgibG9nIikNCnBsdC54bGFiZWwoIkFscGhhIChMYXBsYWNlIFNtb290aGluZykiKQ0KcGx0LnlsYWJlbCgiTWVhbiBBY2N1cmFjeSIpDQpwbHQudGl0bGUoIkdyaWQgU2VhcmNoOiBBbHBoYSB2cyBBY2N1cmFjeSAoTmHDr3ZlIEJheWVzKSIpDQpwbHQuZ3JpZChUcnVlKQ0KcGx0LnNob3coKQ0KDQojICoqSGVhdG1hcCBWaXN1YWxpemF0aW9uKioNCmhlYXRtYXBfZGF0YSA9IHBkLkRhdGFGcmFtZShncmlkX3NlYXJjaF9uYi5jdl9yZXN1bHRzX1sibWVhbl90ZXN0X3Njb3JlIl0ucmVzaGFwZSgtMSwgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXg9Z3JpZF9zZWFyY2hfbmIuY3ZfcmVzdWx0c19bInBhcmFtX2FscGhhIl0uZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPVsiTWVhbiBBY2N1cmFjeSJdKQ0KDQpwbHQuZmlndXJlKGZpZ3NpemU9KDgsIDUpKQ0Kc25zLmhlYXRtYXAoaGVhdG1hcF9kYXRhLCBhbm5vdD1UcnVlLCBjbWFwPSJjb29sd2FybSIsIGZtdD0iLjRmIikNCnBsdC54bGFiZWwoIk1lYW4gQWNjdXJhY3kiKQ0KcGx0LnlsYWJlbCgiQWxwaGEgVmFsdWUiKQ0KcGx0LnRpdGxlKCJHcmlkIFNlYXJjaCBIZWF0bWFwOiBOYcOvdmUgQmF5ZXMiKQ0KcGx0LnNob3coKQ0KDQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgcm9jX2N1cnZlLCBhdWMNCg0KIyBHZXQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMNCnlfcHJvYiA9IGZpbmFsX25iLnByZWRpY3RfcHJvYmEoWF90ZXN0KVs6LCAxXSAgIyBQcm9iYWJpbGl0eSBmb3Igc3BhbSBjbGFzcw0KDQojIENvbXB1dGUgUk9DIGN1cnZlDQpmcHIsIHRwciwgXyA9IHJvY19jdXJ2ZSh5X3Rlc3QsIHlfcHJvYikNCnJvY19hdWMgPSBhdWMoZnByLCB0cHIpDQoNCiMgUGxvdCBST0MgQ3VydmUNCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCwgNSkpDQpwbHQucGxvdChmcHIsIHRwciwgY29sb3I9ImJsdWUiLCBsdz0yLCBsYWJlbD1mIlJPQyBDdXJ2ZSAoQVVDID0ge3JvY19hdWM6LjJmfSkiKQ0KcGx0LnBsb3QoWzAsIDFdLCBbMCwgMV0sIGxpbmVzdHlsZT0iLS0iLCBjb2xvcj0iZ3JheSIpICAjIFJhbmRvbSBndWVzcyBsaW5lDQpwbHQueGxhYmVsKCJGYWxzZSBQb3NpdGl2ZSBSYXRlIikNCnBsdC55bGFiZWwoIlRydWUgUG9zaXRpdmUgUmF0ZSIpDQpwbHQudGl0bGUoIlJPQyBDdXJ2ZTogTmHDr3ZlIEJheWVzIFNwYW0gRGV0ZWN0aW9uIikNCnBsdC5sZWdlbmQoKQ0KcGx0LmdyaWQoVHJ1ZSkNCnBsdC5zaG93KCkNCiBmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgY29uZnVzaW9uX21hdHJpeCwgQ29uZnVzaW9uTWF0cml4RGlzcGxheQ0KDQojIEdlbmVyYXRlIGNvbmZ1c2lvbiBtYXRyaXgNCmNvbmZfbWF0cml4ID0gY29uZnVzaW9uX21hdHJpeCh5X3Rlc3QsIHlfcHJlZCkNCg0KIyBEaXNwbGF5IGl0DQpkaXNwID0gQ29uZnVzaW9uTWF0cml4RGlzcGxheShjb25mX21hdHJpeCwgZGlzcGxheV9sYWJlbHM9WyJIYW0iLCAiU3BhbSJdKQ0KZGlzcC5wbG90KGNtYXA9IkJsdWVzIikNCnBsdC50aXRsZSgiQ29uZnVzaW9uIE1hdHJpeDogTmHDr3ZlIEJheWVzIikNCnBsdC5zaG93KCkNCiBpbXBvcnQgbnVtcHkgYXMgbnANCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KZnJvbSBza2xlYXJuLmNsdXN0ZXIgaW1wb3J0IEtNZWFucywgREJTQ0FODQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgc2lsaG91ZXR0ZV9zY29yZSwgYWRqdXN0ZWRfcmFuZF9zY29yZQ0KZnJvbSBza2xlYXJuLmRlY29tcG9zaXRpb24gaW1wb3J0IFBDQQ0KZnJvbSBza2xlYXJuLm1hbmlmb2xkIGltcG9ydCBUU05FDQpmcm9tIGltYmxlYXJuLm92ZXJfc2FtcGxpbmcgaW1wb3J0IFJhbmRvbU92ZXJTYW1wbGVyDQoNCiMgRGF0YSBBdWdtZW50YXRpb24NCnJvcyA9IFJhbmRvbU92ZXJTYW1wbGVyKHJhbmRvbV9zdGF0ZT00MikgDQpYX2F1Z21lbnRlZCwgeV9hdWdtZW50ZWQgPSByb3MuZml0X3Jlc2FtcGxlKFgsIHkpDQoNCg0KIyBBcHBseSBLLU1lYW5zIHdpdGggdGhlIGJlc3QgayBmcm9tIHByZXZpb3VzIHR1bmluZw0KYmVzdF9rID0gOSAgIyBCYXNlZCBvbiBwcmV2aW91cyBzaWxob3VldHRlIHR1bmluZw0Ka21lYW5zID0gS01lYW5zKG5fY2x1c3RlcnM9YmVzdF9rLCByYW5kb21fc3RhdGU9NDIsIG5faW5pdD0xMCkNCmttZWFuc19sYWJlbHMgPSBrbWVhbnMuZml0X3ByZWRpY3QoWF9hdWdtZW50ZWQpDQoNCiMgQ29tcHV0ZSBTaWxob3VldHRlIFNjb3JlIGZvciBLLU1lYW5zDQprbWVhbnNfc2lsaG91ZXR0ZSA9IHNpbGhvdWV0dGVfc2NvcmUoWF9hdWdtZW50ZWQsIGttZWFuc19sYWJlbHMpDQoNCiMgQXBwbHkgREJTQ0FOIHdpdGggYmVzdCBwYXJhbWV0ZXJzIGZvdW5kIGVhcmxpZXINCmJlc3RfZXBzID0gMC41ICAjIEV4YW1wbGUgYmVzdCBlcHNpbG9uIGZvdW5kDQpiZXN0X21pbl9zYW1wbGVzID0gNSAgIyBFeGFtcGxlIGJlc3QgbWluX3NhbXBsZXMgZm91bmQNCmRic2NhbiA9IERCU0NBTihlcHM9YmVzdF9lcHMsIG1pbl9zYW1wbGVzPWJlc3RfbWluX3NhbXBsZXMpDQpkYnNjYW5fbGFiZWxzID0gZGJzY2FuLmZpdF9wcmVkaWN0KFhfYXVnbWVudGVkKQ0KDQojIENvbXB1dGUgU2lsaG91ZXR0ZSBTY29yZSBmb3IgREJTQ0FOIChleGNsdWRpbmcgbm9pc2UgcG9pbnRzKQ0KdmFsaWRfcG9pbnRzID0gZGJzY2FuX2xhYmVscyAhPSAtMSAgIyBJZ25vcmUgbm9pc2UgcG9pbnRzDQppZiBucC5zdW0odmFsaWRfcG9pbnRzKSA+IDE6ICAjIEVuc3VyZSB3ZSBoYXZlIGVub3VnaCBwb2ludHMgdG8gY29tcHV0ZSBzaWxob3VldHRlDQogICAgZGJzY2FuX3NpbGhvdWV0dGUgPSBzaWxob3VldHRlX3Njb3JlKFhfYXVnbWVudGVkW3ZhbGlkX3BvaW50c10sIGRic2Nhbl9sYWJlbHNbdmFsaWRfcG9pbnRzXSkNCmVsc2U6DQogICAgZGJzY2FuX3NpbGhvdWV0dGUgPSBOb25lDQoNCiMgQ29tcHV0ZSBBZGp1c3RlZCBSYW5kIEluZGV4IHRvIGNvbXBhcmUgY2x1c3RlcnMgdnMgdHJ1ZSBsYWJlbHMNCmttZWFuc19hcmkgPSBhZGp1c3RlZF9yYW5kX3Njb3JlKHlfYXVnbWVudGVkLCBrbWVhbnNfbGFiZWxzKQ0KZGJzY2FuX2FyaSA9IGFkanVzdGVkX3JhbmRfc2NvcmUoeV9hdWdtZW50ZWQsIGRic2Nhbl9sYWJlbHMpDQoNCiMgRGlzcGxheSBSZXN1bHRzDQpzaWxob3VldHRlX3Jlc3VsdHMgPSB7DQogICAgIkstTWVhbnMgU2lsaG91ZXR0ZSBTY29yZSI6IGttZWFuc19zaWxob3VldHRlLA0KICAgICJEQlNDQU4gU2lsaG91ZXR0ZSBTY29yZSI6IGRic2Nhbl9zaWxob3VldHRlIGlmIGRic2Nhbl9zaWxob3VldHRlIGVsc2UgIk4vQSAodG9vIG1hbnkgbm9pc2UgcG9pbnRzKSIsDQogICAgIkstTWVhbnMgQWRqdXN0ZWQgUmFuZCBJbmRleCAoQVJJKSI6IGttZWFuc19hcmksDQogICAgIkRCU0NBTiBBZGp1c3RlZCBSYW5kIEluZGV4IChBUkkpIjogZGJzY2FuX2FyaQ0KfQ0KDQpzaWxob3VldHRlX3Jlc3VsdHMNCiAjIFJldHJ5IHZpc3VhbGl6YXRpb24gdXNpbmcgb25seSBQQ0EgKGZhc3RlciB0aGFuIHQtU05FKQ0KZmlnLCBheGVzID0gcGx0LnN1YnBsb3RzKDEsIDIsIGZpZ3NpemU9KDEyLCA1KSkNCg0KIyBQQ0EgUHJvamVjdGlvbiBmb3IgSy1NZWFucw0KYXhlc1swXS5zY2F0dGVyKFhfcGNhWzosIDBdLCBYX3BjYVs6LCAxXSwgYz1rbWVhbnNfbGFiZWxzLCBjbWFwPSdjb29sd2FybScsIGFscGhhPTAuNykNCmF4ZXNbMF0uc2V0X3RpdGxlKCJQQ0E6IEstTWVhbnMgQ2x1c3RlcmluZyIpDQoNCiMgUENBIFByb2plY3Rpb24gZm9yIERCU0NBTg0KYXhlc1sxXS5zY2F0dGVyKFhfcGNhWzosIDBdLCBYX3BjYVs6LCAxXSwgYz1kYnNjYW5fbGFiZWxzLCBjbWFwPSdjb29sd2FybScsIGFscGhhPTAuNykNCmF4ZXNbMV0uc2V0X3RpdGxlKCJQQ0E6IERCU0NBTiBDbHVzdGVyaW5nIikNCg0KcGx0LnNob3coKQ0KIGltcG9ydCBudW1weSBhcyBucA0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQpmcm9tIHNrbGVhcm4ubW9kZWxfc2VsZWN0aW9uIGltcG9ydCBTdHJhdGlmaWVkS0ZvbGQsIGNyb3NzX3ZhbF9zY29yZSwgdHJhaW5fdGVzdF9zcGxpdCwgR3JpZFNlYXJjaENWDQpmcm9tIHNrbGVhcm4uZW5zZW1ibGUgaW1wb3J0IFJhbmRvbUZvcmVzdENsYXNzaWZpZXINCmZyb20geGdib29zdCBpbXBvcnQgWEdCQ2xhc3NpZmllcg0KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IGNsYXNzaWZpY2F0aW9uX3JlcG9ydCwgY29uZnVzaW9uX21hdHJpeCwgQ29uZnVzaW9uTWF0cml4RGlzcGxheSwgcm9jX2N1cnZlLCBhdWMNCg0KIyBTcGxpdCBpbnRvIFRyYWluaW5nICYgVGVzdCBzZXRzICg4MCUgdHJhaW4sIDIwJSB0ZXN0KQ0KWF90cmFpbiwgWF90ZXN0LCB5X3RyYWluLCB5X3Rlc3QgPSB0cmFpbl90ZXN0X3NwbGl0KFhfYXVnbWVudGVkLCB5X2F1Z21lbnRlZCwgdGVzdF9zaXplPTAuMiwgc3RyYXRpZnk9eV9hdWdtZW50ZWQsIHJhbmRvbV9zdGF0ZT00MikNCg0KIyBEZWZpbmUgaHlwZXJwYXJhbWV0ZXIgZ3JpZHMNCnJmX3BhcmFtcyA9IHsibl9lc3RpbWF0b3JzIjogWzEwMCwgMjAwXSwgIm1heF9kZXB0aCI6IFsxMCwgMjBdLCAicmFuZG9tX3N0YXRlIjogWzQyXX0NCnhnYl9wYXJhbXMgPSB7Im5fZXN0aW1hdG9ycyI6IFsxMDAsIDIwMF0sICJtYXhfZGVwdGgiOiBbMywgNl0sICJsZWFybmluZ19yYXRlIjogWzAuMSwgMC4wMV0sICJyYW5kb21fc3RhdGUiOiBbNDJdfQ0KDQojIEluaXRpYWxpemUgbW9kZWxzDQpyZiA9IFJhbmRvbUZvcmVzdENsYXNzaWZpZXIoKQ0KeGdiID0gWEdCQ2xhc3NpZmllcih1c2VfbGFiZWxfZW5jb2Rlcj1GYWxzZSwgZXZhbF9tZXRyaWM9ImxvZ2xvc3MiKQ0KDQojIFBlcmZvcm0gR3JpZCBTZWFyY2ggd2l0aCAxMC1mb2xkIENWDQpjdiA9IFN0cmF0aWZpZWRLRm9sZChuX3NwbGl0cz0xMCwgc2h1ZmZsZT1UcnVlLCByYW5kb21fc3RhdGU9NDIpDQoNCnJmX2dyaWQgPSBHcmlkU2VhcmNoQ1YocmYsIHJmX3BhcmFtcywgY3Y9Y3YsIHNjb3Jpbmc9ImFjY3VyYWN5Iiwgbl9qb2JzPS0xKQ0KeGdiX2dyaWQgPSBHcmlkU2VhcmNoQ1YoeGdiLCB4Z2JfcGFyYW1zLCBjdj1jdiwgc2NvcmluZz0iYWNjdXJhY3kiLCBuX2pvYnM9LTEpDQoNCnJmX2dyaWQuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQp4Z2JfZ3JpZC5maXQoWF90cmFpbiwgeV90cmFpbikNCg0KIyBHZXQgYmVzdCBtb2RlbHMNCmJlc3RfcmYgPSByZl9ncmlkLmJlc3RfZXN0aW1hdG9yXw0KYmVzdF94Z2IgPSB4Z2JfZ3JpZC5iZXN0X2VzdGltYXRvcl8NCg0KIyBFdmFsdWF0ZSBvbiBUZXN0IFNldA0KeV9wcmVkX3JmID0gYmVzdF9yZi5wcmVkaWN0KFhfdGVzdCkNCnlfcHJlZF94Z2IgPSBiZXN0X3hnYi5wcmVkaWN0KFhfdGVzdCkNCg0KIyBHZXQgYWNjdXJhY3kgJiBjbGFzc2lmaWNhdGlvbiByZXBvcnRzDQpyZl9yZXBvcnQgPSBjbGFzc2lmaWNhdGlvbl9yZXBvcnQoeV90ZXN0LCB5X3ByZWRfcmYsIHRhcmdldF9uYW1lcz1bIkhhbSIsICJTcGFtIl0pDQp4Z2JfcmVwb3J0ID0gY2xhc3NpZmljYXRpb25fcmVwb3J0KHlfdGVzdCwgeV9wcmVkX3hnYiwgdGFyZ2V0X25hbWVzPVsiSGFtIiwgIlNwYW0iXSkNCg0KIyBDb21wdXRlIENvbmZ1c2lvbiBNYXRyaWNlcw0KcmZfY20gPSBjb25mdXNpb25fbWF0cml4KHlfdGVzdCwgeV9wcmVkX3JmKQ0KeGdiX2NtID0gY29uZnVzaW9uX21hdHJpeCh5X3Rlc3QsIHlfcHJlZF94Z2IpDQoNCiMgUk9DIEN1cnZlIGZvciBSYW5kb20gRm9yZXN0DQp5X3Byb2JfcmYgPSBiZXN0X3JmLnByZWRpY3RfcHJvYmEoWF90ZXN0KVs6LCAxXQ0KZnByX3JmLCB0cHJfcmYsIF8gPSByb2NfY3VydmUoeV90ZXN0LCB5X3Byb2JfcmYpDQpyb2NfYXVjX3JmID0gYXVjKGZwcl9yZiwgdHByX3JmKQ0KDQojIFJPQyBDdXJ2ZSBmb3IgWEdCb29zdA0KeV9wcm9iX3hnYiA9IGJlc3RfeGdiLnByZWRpY3RfcHJvYmEoWF90ZXN0KVs6LCAxXQ0KZnByX3hnYiwgdHByX3hnYiwgXyA9IHJvY19jdXJ2ZSh5X3Rlc3QsIHlfcHJvYl94Z2IpDQpyb2NfYXVjX3hnYiA9IGF1YyhmcHJfeGdiLCB0cHJfeGdiKQ0KDQojIERpc3BsYXkgcmVzdWx0cw0KcmVzdWx0cyA9IHsNCiAgICAiQmVzdCBSRiBNb2RlbCI6IGJlc3RfcmYsDQogICAgIkJlc3QgWEdCIE1vZGVsIjogYmVzdF94Z2IsDQogICAgIlJhbmRvbSBGb3Jlc3QgUmVwb3J0IjogcmZfcmVwb3J0LA0KICAgICJYR0Jvb3N0IFJlcG9ydCI6IHhnYl9yZXBvcnQsDQogICAgIlJGIENvbmZ1c2lvbiBNYXRyaXgiOiByZl9jbSwNCiAgICAiWEdCIENvbmZ1c2lvbiBNYXRyaXgiOiB4Z2JfY20sDQogICAgIlJPQyBBVUMgUkYiOiByb2NfYXVjX3JmLA0KICAgICJST0MgQVVDIFhHQiI6IHJvY19hdWNfeGdiDQp9DQoNCiMgUGxvdCBDb25mdXNpb24gTWF0cmljZXMNCmZpZywgYXhlcyA9IHBsdC5zdWJwbG90cygxLCAyLCBmaWdzaXplPSgxMiwgNSkpDQoNCkNvbmZ1c2lvbk1hdHJpeERpc3BsYXkocmZfY20sIGRpc3BsYXlfbGFiZWxzPVsiSGFtIiwgIlNwYW0iXSkucGxvdChheD1heGVzWzBdLCBjbWFwPSJCbHVlcyIpDQpheGVzWzBdLnNldF90aXRsZSgiQ29uZnVzaW9uIE1hdHJpeCAtIFJhbmRvbSBGb3Jlc3QiKQ0KDQpDb25mdXNpb25NYXRyaXhEaXNwbGF5KHhnYl9jbSwgZGlzcGxheV9sYWJlbHM9WyJIYW0iLCAiU3BhbSJdKS5wbG90KGF4PWF4ZXNbMV0sIGNtYXA9IkJsdWVzIikNCmF4ZXNbMV0uc2V0X3RpdGxlKCJDb25mdXNpb24gTWF0cml4IC0gWEdCb29zdCIpDQoNCnBsdC5zaG93KCkNCg0KIyBQbG90IFJPQyBDdXJ2ZXMNCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCwgNSkpDQpwbHQucGxvdChmcHJfcmYsIHRwcl9yZiwgbGFiZWw9ZiJSRiBST0MgQ3VydmUgKEFVQyA9IHtyb2NfYXVjX3JmOi4yZn0pIiwgY29sb3I9ImJsdWUiKQ0KcGx0LnBsb3QoZnByX3hnYiwgdHByX3hnYiwgbGFiZWw9ZiJYR0IgUk9DIEN1cnZlIChBVUMgPSB7cm9jX2F1Y194Z2I6LjJmfSkiLCBjb2xvcj0icmVkIikNCnBsdC5wbG90KFswLCAxXSwgWzAsIDFdLCBsaW5lc3R5bGU9Ii0tIiwgY29sb3I9ImdyYXkiKQ0KcGx0LnhsYWJlbCgiRmFsc2UgUG9zaXRpdmUgUmF0ZSIpDQpwbHQueWxhYmVsKCJUcnVlIFBvc2l0aXZlIFJhdGUiKQ0KcGx0LnRpdGxlKCJST0MgQ3VydmU6IFJhbmRvbSBGb3Jlc3QgdnMgWEdCb29zdCIpDQpwbHQubGVnZW5kKCkNCnBsdC5ncmlkKFRydWUpDQpwbHQuc2hvdygpDQoNCnJlc3VsdHMNCiAjIFNlbGYgLXRyYWluaW5nIHdpdGggWEdCb29zdCAoaW1wbGVtZW50IHNlbGYtdHJhaW5pbmcgYnkgaXRlcmF0aXZlbHkgYWRkaW5nIHBzZXVkby1sYWJlbGVkIGhpZ2gtY29uZmlkZW5jZSBwcmVkaWN0aW9ucy4pDQppbXBvcnQgbnVtcHkgYXMgbnANCmltcG9ydCBwYW5kYXMgYXMgcGQNCmZyb20gc2tsZWFybi5tb2RlbF9zZWxlY3Rpb24gaW1wb3J0IHRyYWluX3Rlc3Rfc3BsaXQNCmZyb20gc2tsZWFybi5tZXRyaWNzIGltcG9ydCBjbGFzc2lmaWNhdGlvbl9yZXBvcnQsIGNvbmZ1c2lvbl9tYXRyaXgsIENvbmZ1c2lvbk1hdHJpeERpc3BsYXkNCmZyb20geGdib29zdCBpbXBvcnQgWEdCQ2xhc3NpZmllcg0KZnJvbSBza2xlYXJuLnNlbWlfc3VwZXJ2aXNlZCBpbXBvcnQgU2VsZlRyYWluaW5nQ2xhc3NpZmllcg0KDQojIFNwbGl0IGludG8gbGFiZWxlZCAoODAlKSBhbmQgdW5sYWJlbGVkICgyMCUpIGRhdGENClhfbGFiZWxlZCwgWF91bmxhYmVsZWQsIHlfbGFiZWxlZCwgXyA9IHRyYWluX3Rlc3Rfc3BsaXQoWF9hdWdtZW50ZWQsIHlfYXVnbWVudGVkLCB0ZXN0X3NpemU9MC4yLCByYW5kb21fc3RhdGU9NDIpDQoNCiMgSW5pdGlhbGl6ZSBYR0Jvb3N0IGFzIHRoZSBiYXNlIGNsYXNzaWZpZXINCnhnYl9iYXNlID0gWEdCQ2xhc3NpZmllcihuX2VzdGltYXRvcnM9MjAwLCBtYXhfZGVwdGg9NiwgbGVhcm5pbmdfcmF0ZT0wLjEsIHJhbmRvbV9zdGF0ZT00MiwgZXZhbF9tZXRyaWM9ImxvZ2xvc3MiKQ0KDQojIFVzZSBTZWxmLVRyYWluaW5nIHdyYXBwZXIgdG8gYWRkIHBzZXVkby1sYWJlbGluZw0Kc2VsZl90cmFpbmluZ194Z2IgPSBTZWxmVHJhaW5pbmdDbGFzc2lmaWVyKHhnYl9iYXNlLCBjcml0ZXJpb249InRocmVzaG9sZCIsIHRocmVzaG9sZD0wLjk1LCB2ZXJib3NlPVRydWUpDQpzZWxmX3RyYWluaW5nX3hnYi5maXQoWF9sYWJlbGVkLCB5X2xhYmVsZWQpDQoNCiMgRXZhbHVhdGUgb24gdGhlIG9yaWdpbmFsIHRlc3Qgc2V0DQp5X3ByZWRfc2VsZl90cmFpbmluZyA9IHNlbGZfdHJhaW5pbmdfeGdiLnByZWRpY3QoWF90ZXN0KQ0KDQojIEdlbmVyYXRlIGV2YWx1YXRpb24gbWV0cmljcw0KY2xhc3NfcmVwb3J0X3NlbGZfdHJhaW5pbmcgPSBjbGFzc2lmaWNhdGlvbl9yZXBvcnQoeV90ZXN0LCB5X3ByZWRfc2VsZl90cmFpbmluZykNCmNvbmZfbWF0cml4X3NlbGZfdHJhaW5pbmcgPSBjb25mdXNpb25fbWF0cml4KHlfdGVzdCwgeV9wcmVkX3NlbGZfdHJhaW5pbmcpDQoNCiMgRGlzcGxheSByZXN1bHRzDQpkaXNwID0gQ29uZnVzaW9uTWF0cml4RGlzcGxheShjb25mX21hdHJpeF9zZWxmX3RyYWluaW5nLCBkaXNwbGF5X2xhYmVscz1bIkhhbSIsICJTcGFtIl0pDQpkaXNwLnBsb3QoKQ0KcGx0LnRpdGxlKCJDb25mdXNpb24gTWF0cml4OiBTZWxmLVRyYWluaW5nIFhHQm9vc3QiKQ0KcGx0LnNob3coKQ0KDQpwcmludCgiXG4gQ2xhc3NpZmljYXRpb24gUmVwb3J0OiBTZWxmLVRyYWluaW5nIFhHQm9vc3RcbiIsIGNsYXNzX3JlcG9ydF9zZWxmX3RyYWluaW5nKQ0KIGZyb20gc2tsZWFybi5zZW1pX3N1cGVydmlzZWQgaW1wb3J0IExhYmVsU3ByZWFkaW5nDQoNCiMgQXNzdW1lIHdlIGhhdmUgc29tZSB1bmxhYmVsZWQgZW1haWwgZGF0YSAoZS5nLiwgbmV3IGluY29taW5nIGVtYWlscykNCnVubGFiZWxlZF9kYXRhID0gWF90ZXN0LmNvcHkoKQ0KdW5sYWJlbGVkX2xhYmVscyA9IG5wLmZ1bGwoWF90ZXN0LnNoYXBlWzBdLCAtMSkgICMgLTEgbWVhbnMgdW5sYWJlbGVkDQojIENvbWJpbmUgbGFiZWxlZCBhbmQgdW5sYWJlbGVkIGRhdGENClhfY29tYmluZWQgPSBucC52c3RhY2soKFhfdHJhaW4udG9hcnJheSgpLCB1bmxhYmVsZWRfZGF0YS50b2FycmF5KCkpKQ0KeV9jb21iaW5lZCA9IG5wLmhzdGFjaygoeV90cmFpbiwgdW5sYWJlbGVkX2xhYmVscykpDQoNCiMgQXBwbHkgU2VtaS1TdXBlcnZpc2VkIExlYXJuaW5nIChMYWJlbCBTcHJlYWRpbmcpDQpzZW1pX3N1cGVydmlzZWRfbW9kZWwgPSBMYWJlbFNwcmVhZGluZyhrZXJuZWw9InJiZiIsIGFscGhhPTAuMikNCnNlbWlfc3VwZXJ2aXNlZF9tb2RlbC5maXQoWF9jb21iaW5lZCwgeV9jb21iaW5lZCkNCg0KIyBQcmVkaWN0IHVzaW5nIHRoZSBzZW1pLXN1cGVydmlzZWQgbW9kZWwNCnlfcHJlZF9zZW1pID0gc2VtaV9zdXBlcnZpc2VkX21vZGVsLnByZWRpY3QoWF90ZXN0KQ0KDQojIEV2YWx1YXRlIHBlcmZvcm1hbmNlDQpzZW1pX3JlcG9ydCA9IGNsYXNzaWZpY2F0aW9uX3JlcG9ydCh5X3Rlc3QsIHlfcHJlZF9zZW1pLCB0YXJnZXRfbmFtZXM9WyJIYW0iLCAiU3BhbSJdKQ0KcHJpbnQoIlxuU2VtaS1TdXBlcnZpc2VkIExlYXJuaW5nIENsYXNzaWZpY2F0aW9uIFJlcG9ydDpcbiIsIHNlbWlfcmVwb3J0KQ0KIGZyb20gc2tsZWFybi5tb2RlbF9zZWxlY3Rpb24gaW1wb3J0IEdyaWRTZWFyY2hDVg0KDQojIERlZmluZSBoeXBlcnBhcmFtZXRlciBncmlkDQp4Z2JfcGFyYW1zID0gew0KICAgICJuX2VzdGltYXRvcnMiOiBbNTAsIDEwMCwgMjAwXSwNCiAgICAibWF4X2RlcHRoIjogWzMsIDYsIDldLA0KICAgICJsZWFybmluZ19yYXRlIjogWzAuMDEsIDAuMSwgMC4zXQ0KfQ0KDQojIFBlcmZvcm0gR3JpZCBTZWFyY2gNCmdyaWRfc2VhcmNoX3hnYiA9IEdyaWRTZWFyY2hDVihYR0JDbGFzc2lmaWVyKHVzZV9sYWJlbF9lbmNvZGVyPUZhbHNlLCBldmFsX21ldHJpYz0ibG9nbG9zcyIsIHJhbmRvbV9zdGF0ZT00MiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGdiX3BhcmFtcywgY3Y9Mywgc2NvcmluZz0iYWNjdXJhY3kiLCBuX2pvYnM9LTEpDQpncmlkX3NlYXJjaF94Z2IuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQoNCiMgUHJpbnQgYmVzdCBwYXJhbWV0ZXJzDQpwcmludCgiQmVzdCBYR0Jvb3N0IFBhcmFtZXRlcnM6IiwgZ3JpZF9zZWFyY2hfeGdiLmJlc3RfcGFyYW1zXykNCg0KIyBFdmFsdWF0ZSB0aGUgb3B0aW1pemVkIFhHQm9vc3QgbW9kZWwNCmJlc3RfeGdiID0gZ3JpZF9zZWFyY2hfeGdiLmJlc3RfZXN0aW1hdG9yXw0KeGdiX2ZpbmFsX3ByZWQgPSBiZXN0X3hnYi5wcmVkaWN0KFhfdGVzdCkNCg0KeGdiX2ZpbmFsX3JlcG9ydCA9IGNsYXNzaWZpY2F0aW9uX3JlcG9ydCh5X3Rlc3QsIHhnYl9maW5hbF9wcmVkLCB0YXJnZXRfbmFtZXM9WyJIYW0iLCAiU3BhbSJdKQ0KcHJpbnQoIlxuT3B0aW1pemVkIFhHQm9vc3QgQ2xhc3NpZmljYXRpb24gUmVwb3J0OlxuIiwgeGdiX2ZpbmFsX3JlcG9ydCkNCiAgIyBTYXZlIEZpbmFsIE1vZGVsIFBhcmFtZXRlcnMgJiBCZXN0IEh5cGVycGFyYW1ldGVycw0KDQogaW1wb3J0IGpvYmxpYg0KDQojIFNhdmUgdGhlIGZpbmFsIHNlbGYtdHJhaW5pbmcgWEdCb29zdCBtb2RlbA0Kam9ibGliLmR1bXAoc2VsZl90cmFpbmluZ194Z2IsICJzZWxmX3RyYWluaW5nX3hnYl9tb2RlbC5wa2wiKQ0KDQojIFNhdmUgdGhlIHN0YW5kYXJkIFhHQm9vc3QgbW9kZWwgYXMgYSBiYWNrdXANCmpvYmxpYi5kdW1wKHhnYl9maW5hbCwgInhnYl9iYWNrdXBfbW9kZWwucGtsIikNCg0KcHJpbnQoIk1vZGVscyBzYXZlZCBzdWNjZXNzZnVsbHkhIikNCg0KDQojIFNhdmUgdGhlIEJlc3QgSHlwZXJwYXJhbWV0ZXJzDQojIFN0b3JlIGh5cGVycGFyYW1ldGVycyBpbiBhIGRpY3Rpb25hcnkNCmJlc3RfaHlwZXJwYXJhbXMgPSB7DQogICAgIlNlbGYtVHJhaW5pbmcgWEdCb29zdCI6IHsNCiAgICAgICAgIm5fZXN0aW1hdG9ycyI6IDIwMCwNCiAgICAgICAgIm1heF9kZXB0aCI6IDYsDQogICAgICAgICJsZWFybmluZ19yYXRlIjogMC4xLA0KICAgICAgICAiZXZhbF9tZXRyaWMiOiAibG9nbG9zcyIsDQogICAgICAgICJ0aHJlc2hvbGQiOiAwLjk1LCAgIyBQc2V1ZG8tbGFiZWxpbmcgY29uZmlkZW5jZSB0aHJlc2hvbGQNCiAgICB9LA0KICAgICJYR0Jvb3N0IEJhY2t1cCI6IHsNCiAgICAgICAgIm5fZXN0aW1hdG9ycyI6IDIwMCwNCiAgICAgICAgIm1heF9kZXB0aCI6IDYsDQogICAgICAgICJsZWFybmluZ19yYXRlIjogMC4xLA0KICAgICAgICAiZXZhbF9tZXRyaWMiOiAibG9nbG9zcyIsDQogICAgfQ0KfQ0KDQojIFNhdmUgaHlwZXJwYXJhbWV0ZXJzIGFzIEpTT04NCmltcG9ydCBqc29uDQp3aXRoIG9wZW4oImJlc3RfaHlwZXJwYXJhbWV0ZXJzLmpzb24iLCAidyIpIGFzIGY6DQogICAganNvbi5kdW1wKGJlc3RfaHlwZXJwYXJhbXMsIGYsIGluZGVudD00KQ0KDQpwcmludCgiQmVzdCBoeXBlcnBhcmFtZXRlcnMgc2F2ZWQhIikNCiAjIFByZXBhcmUgRGVwbG95bWVudCBQbGFuDQpmcm9tIGZsYXNrIGltcG9ydCBGbGFzaywgcmVxdWVzdCwganNvbmlmeQ0KaW1wb3J0IGpvYmxpYg0KaW1wb3J0IG51bXB5IGFzIG5wDQoNCiMgTG9hZCB0aGUgc2F2ZWQgbW9kZWwNCm1vZGVsID0gam9ibGliLmxvYWQoInNlbGZfdHJhaW5pbmdfeGdiX21vZGVsLnBrbCIpDQoNCmFwcCA9IEZsYXNrKF9fbmFtZV9fKQ0KDQpAYXBwLnJvdXRlKCIvcHJlZGljdCIsIG1ldGhvZHM9WyJQT1NUIl0pDQpkZWYgcHJlZGljdCgpOg0KICAgIGRhdGEgPSByZXF1ZXN0Lmpzb24NCiAgICBYX2lucHV0ID0gbnAuYXJyYXkoZGF0YVsiZmVhdHVyZXMiXSkucmVzaGFwZSgxLCAtMSkNCiAgICBwcmVkaWN0aW9uID0gbW9kZWwucHJlZGljdChYX2lucHV0KQ0KICAgIHJldHVybiBqc29uaWZ5KHsicHJlZGljdGlvbiI6IGludChwcmVkaWN0aW9uWzBdKX0pDQoNCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6DQogICAgYXBwLnJ1bihwb3J0PTUwMDAsIGRlYnVnPVRydWUpDQpgYGANCg==