Objectives
Upon completion of this lesson, you will be able to:
- define the Naive Bayes Classifier algorithm
- know when to use Naive Bayes
- engineer features to be suitable for the algorithm
Introduction
The Naive Bayes classification algorithm is a simple yet powerful technique for constructing classifiers: models that assign class labels to problem instances, represented as vectors of feature values. This approach is based on Bayes’ Theorem, with the “naive” assumption that features are conditionally independent given the class. Despite this strong and often unrealistic assumption, Naive Bayes classifiers have worked well in many complex real-world situations.
Key Concepts of Probability
The Naive Bayes Classifier algorithm is a probabilistic machine learning algorithm. Naturally, some key concepts in probability are necessary to understand how the algorithm works. The video below covers key concepts of probability, including empirical probability, independent and dependent events, conditional probability, and Bayes’ Theorem. These concepts form the foundation for the Naive Bayes classification algorithm.
For additional insights into probability theory, consider:
Bayes’ Theorem
At the core of the Naive Bayes classifier is Bayes’ Theorem, which provides a way to update the probability estimate for a hypothesis as more evidence or information becomes available. Mathematically, Bayes’ Theorem is expressed as:
\[ P(C|X) = \frac{P(X|C) \cdot P(C)}{P(X)} \]
Here, \(P(C|X)\) is the posterior probability of class \(C\) given the feature vector \(X\). \(P(X|C)\) is the likelihood, the probability of observing the feature vector \(X\) given the class \(C\). \(P(C)\) is the prior probability of class \(C\), and \(P(X)\) is the evidence, the total probability of observing the feature vector \(X\).
Naive Assumption
The naive aspect of the Naive Bayes classifier is the assumption that all features are conditionally independent given the class – often termed class independence. This simplifies the computation of the likelihood \(P(X|C)\). If \(X\) is composed of \(n\) features, \(X = (x_1, x_2, \ldots, x_n)\), the likelihood can be written as:
\[ P(X|C) = P(x_1, x_2, \ldots, x_n|C) = \prod_{i=1}^{n} P(x_i|C) \]
This assumption greatly reduces the complexity of the model and makes it feasible to work with high-dimensional data. If the assumption of class independence is not made, then the above calculation is not computationally feasible.
The assumption that all features are conditionally independent given the class is a cornerstone of the Naive Bayes classifier. This assumption is essential for several reasons, primarily related to computational efficiency, simplicity of the model, and feasibility of estimation in high-dimensional spaces.
Building a Naive Bayes Classifier
To build a Naive Bayes classifier, we follow these steps:
- Calculate Prior Probabilities: For each class, calculate the prior probability \(P(C)\) from the training data.
- Calculate Likelihoods: For each feature given each class, calculate the likelihood \(P(x_i|C)\).
- Apply Bayes’ Theorem: Use Bayes’ Theorem to calculate the posterior probability for each class given a new instance.
- Class Prediction: Assign the class with the highest posterior probability to the instance.
Consider a simple example of text classification, where we want to classify emails as “spam” or “not spam” based on the presence of certain words.
Step 1: Calculate Prior Probabilities
Let’s assume our training dataset has 100 emails, 30 of which are spam and 70 are not spam. The prior probabilities are:
\[ P(\text{spam}) = \frac{30}{100} = 0.3 \] \[ P(\text{not spam}) = \frac{70}{100} = 0.7 \]
Step 2: Calculate Likelihoods
Suppose we are considering two features: the presence of the word “offer” and the word “click”. From the training data, we calculate the likelihoods:
\[ P(\text{offer}|\text{spam}) = \frac{\text{number of spam emails with "offer"}}{\text{total number of spam emails}} \]
\[ P(\text{click}|\text{spam}) = \frac{\text{number of spam emails with "click"}}{\text{total number of spam emails}} \]
Similarly, for non-spam emails:
\[ P(\text{offer}|\text{not spam}) = \frac{\text{number of not spam emails with "offer"}}{\text{total number of not spam emails}} \]
\[ P(\text{click}|\text{not spam}) = \frac{\text{number of not spam emails with "click"}}{\text{total number of not spam emails}} \]
Step 3: Apply Bayes’ Theorem
Given a new email with the words “offer” and “click”, we calculate the posterior probabilities for each class. For spam:
\[ P(\text{spam}|\text{offer}, \text{click}) = \frac{P(\text{offer}|\text{spam}) \cdot P(\text{click}|\text{spam}) \cdot P(\text{spam})}{P(\text{offer}) \cdot P(\text{click})} \]
For not spam:
\[ P(\text{not spam}|\text{offer}, \text{click}) = \frac{P(\text{offer}|\text{not spam}) \cdot P(\text{click}|\text{not spam}) \cdot P(\text{not spam})}{P(\text{offer}) \cdot P(\text{click})} \]
Step 4: Class Prediction
Compare the posterior probabilities and classify the email as the class with the higher probability.
Mathematical Definition of Naive Bayes
Formally, given a feature vector \(X = (x_1, x_2, \ldots, x_n)\) and a set of classes \(C = \{c_1, c_2, \ldots, c_k\}\), the classifier assigns a class label \(\hat{y}\) according to:
\[ \hat{y} = \arg \max_{c \in C} P(C=c) \prod_{i=1}^{n} P(X_i=x_i|C=c) \]
This formulation assumes conditional independence of the features \(x_i\).
Practical Considerations
- Laplace Smoothing: In practice, we often use Laplace smoothing to handle the problem of zero probabilities. This involves adding a small value (typically 1) to the count of each feature’s occurrences.
- Multinomial and Gaussian Naive Bayes: The Naive Bayes classifier can be adapted to different types of data. For discrete features (e.g., word counts), the Multinomial Naive Bayes is used. For continuous features (e.g., real-valued measurements), the Gaussian Naive Bayes is more appropriate, assuming a normal distribution of the features.
Implementation Example in R
Here is an example of implementing a Naive Bayes classifier using the e1071
package in R for a text classification task.
# Load necessary library
library(e1071)
# Sample data
data <- data.frame(
text = c("offer is secret", "click secret link", "secret sports link", "sports link is available"),
class = as.factor(c("spam", "spam", "not_spam", "not_spam"))
)
# Create a Document-Term Matrix
library(tm)
corpus <- Corpus(VectorSource(data$text))
dtm <- DocumentTermMatrix(corpus)
dtm <- as.data.frame(as.matrix(dtm))
# Combine the DTM with the class labels
train_data <- cbind(dtm, class = data$class)
# Train the Naive Bayes classifier
model <- naiveBayes(class ~ ., data = train_data)
# Predict on new data
new_text <- c("sports is secret", "offer link available")
new_corpus <- Corpus(VectorSource(new_text))
new_dtm <- DocumentTermMatrix(new_corpus)
new_dtm <- as.data.frame(as.matrix(new_dtm))
predictions <- predict(model, new_dtm)
print(predictions)
## [1] not_spam not_spam
## Levels: not_spam spam
This code demonstrates the training of a Naive Bayes classifier on a simple dataset and how to use it for prediction. The e1071
package provides a straightforward interface for creating and using Naive Bayes classifiers in R.
Computational Assumptions
Without the assumption of conditional independence, the computation of the joint probability \(P(X|C)\) would be extremely complex. For a feature vector \(X = (x_1, x_2, \ldots, x_n)\), the joint probability without the independence assumption would require modeling the full joint distribution of the features given the class:
\[ P(X|C) = P(x_1, x_2, \ldots, x_n|C) \]
This requires estimating the probabilities of all possible combinations of feature values for each class, which is computationally unfeasible for large \(n\). The number of parameters required would grow exponentially with the number of features, making the model prone to overfitting, especially with limited training data.
By assuming conditional independence, the joint probability simplifies to the product of individual probabilities:
\[ P(X|C) = \prod_{i=1}^{n} P(x_i|C) \]
This simplification drastically reduces the number of parameters to estimate, making the model both computationally efficient and scalable to high-dimensional data. Each feature can be treated independently, and the overall likelihood is a simple product of individual likelihoods.
Feasibility of Parameter Estimation
In practical terms, the amount of data required to reliably estimate the joint distribution of all features would be prohibitively large if the features were not assumed to be independent. For instance, if each feature can take \(m\) possible values, the number of parameters required to describe the joint distribution of \(n\) features is \(m^n\). In contrast, under the conditional independence assumption, the number of parameters is reduced to \(n \cdot m\), which is linear in the number of features.
Computational Efficiency
The independence assumption allows the Naive Bayes classifier to be computationally efficient both in terms of training and inference. The training process involves estimating the prior probabilities \(P(C)\) and the conditional probabilities \(P(x_i|C)\), which can be done with simple counting and normalization. This makes the training phase very fast compared to more complex models that require iterative optimization techniques.
During inference, calculating the posterior probability for a given instance involves multiplying the probabilities of individual features, which is computationally very inexpensive. This makes Naive Bayes classifiers particularly suitable for real-time applications where quick decision-making is essential and desirable.
Robustness and Effectiveness
Despite the often unrealistic assumption of feature independence, Naive Bayes classifiers have been found to perform surprisingly well in practice, especially in domains such as text classification and spam filtering. This can be attributed to the fact that the independence assumption often holds well enough to make the model effective, even if not entirely accurate. The errors introduced by the independence assumption tend to cancel out over the many features, leading to good overall performance.
Example: Document Classification
Consider the task of classifying documents based on the presence of certain words. If we did not assume independence, we would need to estimate the probability of every possible combination of words appearing together in a document for each class. This is not feasible due to the enormous number of combinations, especially with a large vocabulary.
With the independence assumption, we only need to estimate the probability of each word appearing in a document given the class. This can be done by simply counting the occurrences of each word in documents of each class. For example, in spam email classification, we assume that the presence of the word “offer” is independent of the presence of the word “click” given the email is spam. This allows us to compute:
\[ P(\text{offer, click}|\text{spam}) = P(\text{offer}|\text{spam}) \cdot P(\text{click}|\text{spam}) \]
This simplification makes it feasible to handle the high dimensionality of the feature space (i.e., the large number of unique words in the vocabulary) without requiring an impractical amount of training data.
Common Use Cases for Naive Bayes Classifier
The Naive Bayes algorithm is widely used in various machine learning applications due to its simplicity, efficiency, and effectiveness, especially in scenarios where the assumption of feature independence is reasonably valid. Here are some common use cases:
1. Text Classification
Spam Detection
One of the most well-known applications of Naive Bayes is in spam filtering. Email services use Naive Bayes classifiers to identify spam emails based on the occurrence of specific words and phrases that are common in spam.
Sentiment Analysis
Naive Bayes is often used to classify the sentiment of text, such as determining whether a product review or social media post is positive, negative, or neutral. This involves analyzing the frequency of words associated with different sentiments.
Document Categorization
In news aggregation and content management systems, Naive Bayes classifiers are used to categorize documents into predefined categories such as sports, politics, technology, and entertainment based on the text content.
2. Medical Diagnosis
Naive Bayes classifiers can assist in diagnosing diseases by analyzing patient data, including symptoms and test results. Given the presence of certain symptoms, the algorithm can predict the likelihood of various diseases. This can be particularly useful in scenarios where different symptoms are considered independent given the disease.
3. Image Recognition
While Naive Bayes is not the most common algorithm for image recognition, it can be applied to specific tasks where features extracted from images (such as pixel values or more abstract features) are used to classify images. For instance, it can be used in handwriting recognition to classify digits in scanned documents.
4. Recommendation Systems
Naive Bayes can be used to build recommendation systems that suggest products, movies, or other items to users based on their past behavior and preferences. For example, in a movie recommendation system, the algorithm can classify movies into genres that a user is likely to enjoy based on their viewing history.
5. Real-time Prediction
Due to its computational efficiency, Naive Bayes is suitable for real-time prediction tasks. Applications include real-time fraud detection in financial transactions, where the algorithm can quickly classify transactions as fraudulent or legitimate based on various features.
6. Anomaly Detection
Naive Bayes can be used for anomaly detection in various domains such as network security, where it can classify network activities as normal or suspicious based on features like IP addresses, port numbers, and packet sizes.
7. Recommendation Systems
In collaborative filtering, Naive Bayes can be used to recommend products to users based on their preferences and behaviors. For example, in an e-commerce setting, it can suggest products to users based on their previous purchases and browsing history.
8. Language Processing
Naive Bayes classifiers are used in various natural language processing (NLP) tasks such as language identification, where the algorithm determines the language of a given text, and part-of-speech tagging, where it assigns parts of speech to words in a sentence.
9. Customer Relationship Management (CRM)
In CRM systems, Naive Bayes can be used to classify customer feedback, segment customers based on their behavior, and predict customer churn, helping businesses to devise targeted marketing strategies and improve customer retention.
Example: Spam Detection
To illustrate, let’s consider a simple example of spam detection. Suppose we have a dataset of emails labeled as spam or not spam, and we want to build a Naive Bayes classifier to automatically classify new emails.
Training Data
“Win money now” |
Spam |
“Hello, how are you?” |
Not Spam |
“Special offer just for you” |
Spam |
“Meeting tomorrow” |
Not Spam |
Step-by-Step Process
- Calculate Prior Probabilities:
- \(P(\text{Spam}) = \frac{2}{4} = 0.5\)
- \(P(\text{Not Spam}) = \frac{2}{4} = 0.5\)
- Calculate Likelihoods:
- \(P(\text{Win}|\text{Spam}) = \frac{1}{2}\)
- \(P(\text{money}|\text{Spam}) = \frac{1}{2}\)
- \(P(\text{now}|\text{Spam}) = \frac{1}{2}\)
- \(P(\text{Hello}|\text{Not Spam}) = \frac{1}{2}\)
- \(P(\text{are}|\text{Not Spam}) = \frac{1}{2}\)
- \(P(\text{you}|\text{Not Spam}) = \frac{1}{2}\)
- Classify New Email:
- For a new email “Win money”: \[ P(\text{Spam}|\text{Win money}) = P(\text{Spam}) \cdot P(\text{Win}|\text{Spam}) \cdot P(\text{money}|\text{Spam}) = 0.5 \cdot 0.5 \cdot 0.5 = 0.125 \] \[ P(\text{Not Spam}|\text{Win money}) = P(\text{Not Spam}) \cdot P(\text{Win}|\text{Not Spam}) \cdot P(\text{money}|\text{Not Spam}) = 0.5 \cdot 0 \cdot 0 = 0 \]
- The email “Win money” would be classified as Spam.
Example: Medical Diagnosis
Let’s consider an example of using the Naive Bayes Classifier for diagnosing a medical condition, such as heart disease. In this scenario, the classifier will predict whether a patient has heart disease based on several features such as age, cholesterol level, blood pressure, and presence of certain symptoms.
Dataset
Suppose we have a dataset with the following features for each patient:
- Age
- Cholesterol level
- Blood pressure
- Presence of chest pain
- Exercise-induced angina
Each patient is also labeled as either having heart disease (yes) or not having heart disease (no).
Step-by-Step Process
- Calculate Prior Probabilities:
Let’s assume our training dataset has 100 patients, 40 of whom have heart disease. The prior probabilities therefore are:
\[ P(\text{Heart Disease} = \text{yes}) = \frac{40}{100} = 0.4 \]
\[ P(\text{Heart Disease} = \text{no}) = \frac{60}{100} = 0.6 \]
- Calculate Likelihoods:
We need to calculate the likelihood of each feature given the class. For simplicity, let’s assume our features are categorical (e.g., “high” or “normal” for cholesterol, “yes” or “no” for chest pain).
For example, we calculate the likelihoods for cholesterol levels:
\[ P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{yes}) = \frac{\text{number of patients with high cholesterol and heart disease}}{\text{number of patients with heart disease}} \]
\[ P(\text{Cholesterol} = \text{normal}|\text{Heart Disease} = \text{yes}) = \frac{\text{number of patients with normal cholesterol and heart disease}}{\text{number of patients with heart disease}} \]
And similarly for patients without heart disease:
\[ P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{no}) = \frac{\text{number of patients with high cholesterol and no heart disease}}{\text{number of patients with no heart disease}} \]
\[ P(\text{Cholesterol} = \text{normal}|\text{Heart Disease} = \text{no}) = \frac{\text{number of patients with normal cholesterol and no heart disease}}{\text{number of patients with no heart disease}} \]
Let’s assume the following calculated likelihoods based on our dataset:
- \(P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{yes}) = 0.7\)
- \(P(\text{Cholesterol} = \text{normal}|\text{Heart Disease} = \text{yes}) = 0.3\)
- \(P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{no}) = 0.4\)
- \(P(\text{Cholesterol} = \text{normal}|\text{Heart Disease} = \text{no}) = 0.6\)
We would repeat this process for all of the other features such as age, blood pressure, chest pain, and exercise-induced angina.
- Classify New Patient:
Given a new patient with the following characteristics:
Age: above 50
Cholesterol level: high
Blood pressure: high
Chest pain: yes
Exercise-induced angina: yes
We calculate the posterior probability for both classes (heart disease: yes or no).
For heart disease (yes):
\[
P(\text{Heart Disease} = \text{yes}|\text{features}) = P(\text{Heart Disease} = \text{yes}) \times P(\text{Age} > 50|\text{Heart Disease} = \text{yes}) \times P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{yes}) \times \ldots
\]
For heart disease (no):
\[
P(\text{Heart Disease} = \text{no}|\text{features}) = P(\text{Heart Disease} = \text{no}) \times P(\text{Age} > 50|\text{Heart Disease} = \text{no}) \times P(\text{Cholesterol} = \text{high}|\text{Heart Disease} = \text{no}) \times \ldots
\]
Using our assumed likelihoods and prior probabilities, we calculate these values.
Comparison and Prediction:
- Compare the posterior probabilities:
- If \(P(\text{Heart Disease} = \text{yes}|\text{features}) > P(\text{Heart Disease} = \text{no}|\text{features})\), classify the patient as having heart disease.
- Otherwise, classify the patient as not having heart disease.
Implementation Example in R
Here’s a simple R implementation using the e1071
package:
# Load necessary libraries
library(e1071)
# Sample data
data <- data.frame(
age = as.factor(c("above_50", "below_50", "above_50", "below_50", "above_50")),
cholesterol = as.factor(c("high", "normal", "high", "normal", "high")),
bp = as.factor(c("high", "normal", "high", "normal", "high")),
chest_pain = as.factor(c("yes", "no", "yes", "no", "yes")),
exercise_angina = as.factor(c("yes", "no", "yes", "no", "yes")),
heart_disease = as.factor(c("yes", "no", "yes", "no", "yes"))
)
# Train the Naive Bayes classifier
model <- naiveBayes(heart_disease ~ ., data = data)
# Predict on new data
new_patient <- data.frame(
age = factor("above_50", levels = levels(data$age)),
cholesterol = factor("high", levels = levels(data$cholesterol)),
bp = factor("high", levels = levels(data$bp)),
chest_pain = factor("yes", levels = levels(data$chest_pain)),
exercise_angina = factor("yes", levels = levels(data$exercise_angina))
)
prediction <- predict(model, new_patient)
print(prediction)
Numeric Features
The Naive Bayes algorithm requires counting of frequencies so categorical features are necessary. When dealing with numeric rather than categorical features in a dataset for Naive Bayes classification, data scientists have several strategies to adapt the algorithm, which traditionally assumes categorical features. Here are some common approaches:
Gaussian Naive Bayes
For numeric features, the Gaussian Naive Bayes (GNB) is a common variation. It assumes that the numeric features follow a Gaussian (normal) distribution. This version of Naive Bayes uses the following formula to compute the likelihood of the data:
\[ P(x_i|C) = \frac{1}{\sqrt{2\pi\sigma_C^2}} \exp\left( -\frac{(x_i - \mu_C)^2}{2\sigma_C^2} \right) \]
where \(\mu_C\) and \(\sigma_C\) are the mean and standard deviation of the feature \(x_i\) for class \(C\), respectively. During the training phase, the parameters \(\mu_C\) and \(\sigma_C\) are estimated for each feature and each class.
Discretization (Binning)
Another approach is to convert the numeric features into categorical features through a process called discretization or binning. This involves dividing the range of numeric values into discrete intervals and treating each interval as a category. For example, age might be converted into bins such as “0-18”, “19-35”, “36-50”, “51+”.
# Discretize the numeric feature
data$age_bin <- cut(data$age, breaks = c(-Inf, 18, 35, 50, Inf), labels = c("0-18", "19-35", "36-50", "51+"))
# Train Naive Bayes model using the binned feature
model <- naiveBayes(heart_disease ~ age_bin + cholesterol + bp + chest_pain + exercise_angina, data = data)
Kernel Density Estimation
For numeric features that do not follow a normal distribution, kernel density estimation (KDE) can be used to estimate the probability density function of the feature. This non-parametric approach is more flexible than assuming a specific distribution like Gaussian.
Handling Mixed Features
In many real-world datasets, you’ll have both categorical and numeric features. In such cases, it’s common to use a mixed approach where categorical features are handled with traditional Naive Bayes methods, and numeric features are handled using Gaussian Naive Bayes or another suitable method.
Example in R
Here’s how to implement Gaussian Naive Bayes in R using the e1071
package which automatically adjusts the algorithm:
# Load necessary libraries
library(e1071)
# Sample data
data <- data.frame(
age = c(25, 45, 35, 50, 23, 34),
cholesterol = c(200, 250, 180, 210, 190, 240),
bp = c(130, 140, 120, 150, 110, 135),
chest_pain = as.factor(c("yes", "no", "yes", "no", "yes", "no")),
exercise_angina = as.factor(c("no", "yes", "no", "yes", "no", "yes")),
heart_disease = as.factor(c("yes", "no", "yes", "no", "yes", "no"))
)
# Train the Gaussian Naive Bayes classifier
model <- naiveBayes(heart_disease ~ age + cholesterol + bp + chest_pain + exercise_angina, data = data)
# Predict on new data
new_patient <- data.frame(
age = 45,
cholesterol = 220,
bp = 140,
chest_pain = factor("yes", levels = levels(data$chest_pain)),
exercise_angina = factor("no", levels = levels(data$exercise_angina))
)
prediction <- predict(model, new_patient)
print(prediction)
## [1] yes
## Levels: no yes
Conclusion
Adapting Naive Bayes for numeric features involves using techniques like Gaussian Naive Bayes, discretization, or kernel density estimation. Each approach has its strengths and is suitable for different types of data and applications. By selecting the appropriate method, data scientists can effectively apply Naive Bayes to datasets with numeric features.
Summary
The Naive Bayes classification algorithm, despite its simplicity and the naive assumption of feature independence, remains a popular and effective method for many classification tasks. Its foundation in Bayes’ Theorem and the resulting computational efficiency make it especially suitable for large-scale problems. Understanding its theoretical underpinnings and practical applications is essential for any machine learning practitioner.
The conditional independence assumption in the Naive Bayes classifier is essential for making the model computationally efficient, scalable, and feasible to train with realistic amounts of data. While this assumption is often not strictly true, the resulting model often performs well in practice due to the overall robustness of the approach and the tendency of errors to cancel out across many features. This makes the Naive Bayes classifier a valuable tool in the machine learning toolbox, especially for high-dimensional data and applications requiring quick and efficient classification.
The Naive Bayes Classifier algorithm is quite versatile and can be applied to a wide range of applications, particularly in domains where feature independence can be reasonably assumed or the benefits of the algorithm’s efficiency outweigh the potential inaccuracies introduced by the independence assumption. This makes Naive Bayes a valuable tool in the machine learning practitioner’s arsenal, especially in the areas of text classification, anomaly detection, and medical diagnostics.
References
No references.
LS0tCnRpdGxlOiAiVGhlIE5haXZlIEJheWVzIENsYXNzaWZpZXIgQWxnb3JpdGhtIGZvciBCaW5hcnkgQ2xhc3NpZmljYXRpb24iCnBhcmFtczoKICBjYXRlZ29yeTogMwogIHN0YWNrczogMAogIG51bWJlcjogNDIwCiAgdGltZTogOTAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiBuYWl2ZSBiYXllcyxiYXllcyxtYWNoaW5lIGxlYXJuaW5nLGNsYXNzaWZpY2F0aW9uLHNwYW0gZGV0ZWN0aW9uCiAgZGVzY3JpcHRpb246ICJFeHBsYWlucyB0aGUgTmFpdmUgQmF5ZXMgQ2xhc3NpZmllciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgCiAgICAgICAgICAgICAgICBhbGdvcml0aG0gZm9yIHByZWRpY3RpbmcgYSBiaW5hcnkgY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlCiAgICAgICAgICAgICAgICAoY2xhc3NpZmljYXRpb24pLiBEZW1vbnN0cmF0ZXMgdGhlIGFsZ29yaXRobXMgdXNlIHRocm91Z2gKICAgICAgICAgICAgICAgIGltcGxlbWVudGF0aW9ucyBmcm9tIHZhcmlvdXMgcGFja2FnZXMgaW5jbHVkaW5nIGUxMDcxIGFuZCBrbGFSLgogICAgICAgICAgICAgICAgU2hvd3MgaG93IHRvIGJpbiBudW1lcmljIGZlYXR1cmVzIGludG8gY2F0ZWdvcmljYWwgZmVhdHVyZXMuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIE9iamVjdGl2ZXMKClVwb24gY29tcGxldGlvbiBvZiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgYmUgYWJsZSB0bzoKCi0gICBkZWZpbmUgdGhlICpOYWl2ZSBCYXllcyBDbGFzc2lmaWVyKiBhbGdvcml0aG0KLSAgIGtub3cgd2hlbiB0byB1c2UgKk5haXZlIEJheWVzKgotICAgZW5naW5lZXIgZmVhdHVyZXMgdG8gYmUgc3VpdGFibGUgZm9yIHRoZSBhbGdvcml0aG0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgSW50cm9kdWN0aW9uCgpUaGUgTmFpdmUgQmF5ZXMgY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtIGlzIGEgc2ltcGxlIHlldCBwb3dlcmZ1bCB0ZWNobmlxdWUgZm9yIGNvbnN0cnVjdGluZyBjbGFzc2lmaWVyczogbW9kZWxzIHRoYXQgYXNzaWduIGNsYXNzIGxhYmVscyB0byBwcm9ibGVtIGluc3RhbmNlcywgcmVwcmVzZW50ZWQgYXMgdmVjdG9ycyBvZiBmZWF0dXJlIHZhbHVlcy4gVGhpcyBhcHByb2FjaCBpcyBiYXNlZCBvbiBCYXllcycgVGhlb3JlbSwgd2l0aCB0aGUgIm5haXZlIiBhc3N1bXB0aW9uIHRoYXQgZmVhdHVyZXMgYXJlIGNvbmRpdGlvbmFsbHkgaW5kZXBlbmRlbnQgZ2l2ZW4gdGhlIGNsYXNzLiBEZXNwaXRlIHRoaXMgc3Ryb25nIGFuZCBvZnRlbiB1bnJlYWxpc3RpYyBhc3N1bXB0aW9uLCBOYWl2ZSBCYXllcyBjbGFzc2lmaWVycyBoYXZlIHdvcmtlZCB3ZWxsIGluIG1hbnkgY29tcGxleCByZWFsLXdvcmxkIHNpdHVhdGlvbnMuCgojIyBLZXkgQ29uY2VwdHMgb2YgUHJvYmFiaWxpdHkKClRoZSBOYWl2ZSBCYXllcyBDbGFzc2lmaWVyIGFsZ29yaXRobSBpcyBhIHByb2JhYmlsaXN0aWMgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG0uIE5hdHVyYWxseSwgc29tZSBrZXkgY29uY2VwdHMgaW4gcHJvYmFiaWxpdHkgYXJlIG5lY2Vzc2FyeSB0byB1bmRlcnN0YW5kIGhvdyB0aGUgYWxnb3JpdGhtIHdvcmtzLiBUaGUgdmlkZW8gYmVsb3cgY292ZXJzIGtleSBjb25jZXB0cyBvZiBwcm9iYWJpbGl0eSwgaW5jbHVkaW5nIGVtcGlyaWNhbCBwcm9iYWJpbGl0eSwgaW5kZXBlbmRlbnQgYW5kIGRlcGVuZGVudCBldmVudHMsIGNvbmRpdGlvbmFsIHByb2JhYmlsaXR5LCBhbmQgQmF5ZXMnIFRoZW9yZW0uIFRoZXNlIGNvbmNlcHRzIGZvcm0gdGhlIGZvdW5kYXRpb24gZm9yIHRoZSBOYWl2ZSBCYXllcyBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0uCgo8aWZyYW1lIHRpdGxlPSJCYXNpYyBDb25jZXB0cyBvZiBQcm9iYWJpbGl0eSBmb3IgTWFjaGluZSBMZWFybmluZyIgc3JjPSJodHRwczovL3BsYXllci52aW1lby5jb20vdmlkZW8vODMzNDYzODY4P2g9YmZmMTMyOWEzZiZhbXA7dGl0bGU9MCZhbXA7YnlsaW5lPTAmYW1wO3BvcnRyYWl0PTAmYW1wO3NwZWVkPTAmYW1wO2JhZGdlPTAmYW1wO2F1dG9wYXVzZT0wJmFtcDtwbGF5ZXJfaWQ9MCZhbXA7YXBwX2lkPTU4NDc5IiB3aWR0aD0iNDgwIiBoZWlnaHQ9IjM2MCIgYWxsb3dmdWxsc2NyZWVuPSJhbGxvd2Z1bGxzY3JlZW4iIGFsbG93PSJhdXRvcGxheTsgZnVsbHNjcmVlbjsgcGljdHVyZS1pbi1waWN0dXJlIj4KCjwvaWZyYW1lPgoKRm9yIGFkZGl0aW9uYWwgaW5zaWdodHMgaW50byBwcm9iYWJpbGl0eSB0aGVvcnksIGNvbnNpZGVyOgoKLSAgIFsxMDQuMTAxIC0tIEtleSBDb25jZXB0cyBvZiBQcm9iYWJpbGl0eV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8xMDQucHJvYi9sLTEwNC0xMDEtYmFzaWMtcHJvYi9sLTEwNC0xMDEuaHRtbCkKLSAgIFsxMDQuMTUxIC0tIEFuYWx5emluZyB0aGUgQ2F1c2VzIG9mIEV2ZW50czogQmF5ZXMnIFRoZW9yZW1dKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMTA0LnByb2IvbC0xMDQtMTUxLWJheWVzLXRoZW9yZW0vbC0xMDQtMTUxLmh0bWwpCi0gICBbTGFuZSwgRC4sIE9zaGVyc29uLCBELiBPbmxpbmUgU3RhdGlzdGljcyBCb29rLCBTZWN0aW9uIFY6IFByb2JhYmlsaXR5XShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvcHJvYmFiaWxpdHkvcHJvYmFiaWxpdHkuaHRtbCkuCgojIyBMZWN0dXJlCgoqKlNsaWRlIERlY2sqKjogW3MtMy00MjAtbmFpdmUtYmF5ZXMtY2xhc3NpZmllci5wcHR4XShzLTMtNDIwLW5haXZlLWJheWVzLWNsYXNzaWZpZXIucHB0eCkKCiMjIEJheWVzJyBUaGVvcmVtCgpBdCB0aGUgY29yZSBvZiB0aGUgTmFpdmUgQmF5ZXMgY2xhc3NpZmllciBpcyBCYXllcycgVGhlb3JlbSwgd2hpY2ggcHJvdmlkZXMgYSB3YXkgdG8gdXBkYXRlIHRoZSBwcm9iYWJpbGl0eSBlc3RpbWF0ZSBmb3IgYSBoeXBvdGhlc2lzIGFzIG1vcmUgZXZpZGVuY2Ugb3IgaW5mb3JtYXRpb24gYmVjb21lcyBhdmFpbGFibGUuIE1hdGhlbWF0aWNhbGx5LCBCYXllcycgVGhlb3JlbSBpcyBleHByZXNzZWQgYXM6CgokJCBQKEN8WCkgPSBcZnJhY3tQKFh8QykgXGNkb3QgUChDKX17UChYKX0gJCQKCkhlcmUsICRQKEN8WCkkIGlzIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgY2xhc3MgJEMkIGdpdmVuIHRoZSBmZWF0dXJlIHZlY3RvciAkWCQuICRQKFh8QykkIGlzIHRoZSBsaWtlbGlob29kLCB0aGUgcHJvYmFiaWxpdHkgb2Ygb2JzZXJ2aW5nIHRoZSBmZWF0dXJlIHZlY3RvciAkWCQgZ2l2ZW4gdGhlIGNsYXNzICRDJC4gJFAoQykkIGlzIHRoZSBwcmlvciBwcm9iYWJpbGl0eSBvZiBjbGFzcyAkQyQsIGFuZCAkUChYKSQgaXMgdGhlIGV2aWRlbmNlLCB0aGUgdG90YWwgcHJvYmFiaWxpdHkgb2Ygb2JzZXJ2aW5nIHRoZSBmZWF0dXJlIHZlY3RvciAkWCQuCgojIyBOYWl2ZSBBc3N1bXB0aW9uCgpUaGUgbmFpdmUgYXNwZWN0IG9mIHRoZSBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyIGlzIHRoZSBhc3N1bXB0aW9uIHRoYXQgYWxsIGZlYXR1cmVzIGFyZSBjb25kaXRpb25hbGx5IGluZGVwZW5kZW50IGdpdmVuIHRoZSBjbGFzcyAtLSBvZnRlbiB0ZXJtZWQgKmNsYXNzIGluZGVwZW5kZW5jZSouIFRoaXMgc2ltcGxpZmllcyB0aGUgY29tcHV0YXRpb24gb2YgdGhlIGxpa2VsaWhvb2QgJFAoWHxDKSQuIElmICRYJCBpcyBjb21wb3NlZCBvZiAkbiQgZmVhdHVyZXMsICRYID0gKHhfMSwgeF8yLCBcbGRvdHMsIHhfbikkLCB0aGUgbGlrZWxpaG9vZCBjYW4gYmUgd3JpdHRlbiBhczoKCiQkIFAoWHxDKSA9IFAoeF8xLCB4XzIsIFxsZG90cywgeF9ufEMpID0gXHByb2Rfe2k9MX1ee259IFAoeF9pfEMpICQkCgpUaGlzIGFzc3VtcHRpb24gZ3JlYXRseSByZWR1Y2VzIHRoZSBjb21wbGV4aXR5IG9mIHRoZSBtb2RlbCBhbmQgbWFrZXMgaXQgZmVhc2libGUgdG8gd29yayB3aXRoIGhpZ2gtZGltZW5zaW9uYWwgZGF0YS4gSWYgdGhlIGFzc3VtcHRpb24gb2YgY2xhc3MgaW5kZXBlbmRlbmNlIGlzIG5vdCBtYWRlLCB0aGVuIHRoZSBhYm92ZSBjYWxjdWxhdGlvbiBpcyBub3QgY29tcHV0YXRpb25hbGx5IGZlYXNpYmxlLgoKVGhlIGFzc3VtcHRpb24gdGhhdCBhbGwgZmVhdHVyZXMgYXJlIGNvbmRpdGlvbmFsbHkgaW5kZXBlbmRlbnQgZ2l2ZW4gdGhlIGNsYXNzIGlzIGEgY29ybmVyc3RvbmUgb2YgdGhlIE5haXZlIEJheWVzIGNsYXNzaWZpZXIuIFRoaXMgYXNzdW1wdGlvbiBpcyBlc3NlbnRpYWwgZm9yIHNldmVyYWwgcmVhc29ucywgcHJpbWFyaWx5IHJlbGF0ZWQgdG8gY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LCBzaW1wbGljaXR5IG9mIHRoZSBtb2RlbCwgYW5kIGZlYXNpYmlsaXR5IG9mIGVzdGltYXRpb24gaW4gaGlnaC1kaW1lbnNpb25hbCBzcGFjZXMuCgojIyBCdWlsZGluZyBhIE5haXZlIEJheWVzIENsYXNzaWZpZXIKClRvIGJ1aWxkIGEgTmFpdmUgQmF5ZXMgY2xhc3NpZmllciwgd2UgZm9sbG93IHRoZXNlIHN0ZXBzOgoKMS4gICoqQ2FsY3VsYXRlIFByaW9yIFByb2JhYmlsaXRpZXM6KiogRm9yIGVhY2ggY2xhc3MsIGNhbGN1bGF0ZSB0aGUgcHJpb3IgcHJvYmFiaWxpdHkgJFAoQykkIGZyb20gdGhlIHRyYWluaW5nIGRhdGEuCjIuICAqKkNhbGN1bGF0ZSBMaWtlbGlob29kczoqKiBGb3IgZWFjaCBmZWF0dXJlIGdpdmVuIGVhY2ggY2xhc3MsIGNhbGN1bGF0ZSB0aGUgbGlrZWxpaG9vZCAkUCh4X2l8QykkLgozLiAgKipBcHBseSBCYXllcycgVGhlb3JlbToqKiBVc2UgQmF5ZXMnIFRoZW9yZW0gdG8gY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZm9yIGVhY2ggY2xhc3MgZ2l2ZW4gYSBuZXcgaW5zdGFuY2UuCjQuICAqKkNsYXNzIFByZWRpY3Rpb246KiogQXNzaWduIHRoZSBjbGFzcyB3aXRoIHRoZSBoaWdoZXN0IHBvc3RlcmlvciBwcm9iYWJpbGl0eSB0byB0aGUgaW5zdGFuY2UuCgpDb25zaWRlciBhIHNpbXBsZSBleGFtcGxlIG9mIHRleHQgY2xhc3NpZmljYXRpb24sIHdoZXJlIHdlIHdhbnQgdG8gY2xhc3NpZnkgZW1haWxzIGFzICJzcGFtIiBvciAibm90IHNwYW0iIGJhc2VkIG9uIHRoZSBwcmVzZW5jZSBvZiBjZXJ0YWluIHdvcmRzLgoKIyMjIFN0ZXAgMTogQ2FsY3VsYXRlIFByaW9yIFByb2JhYmlsaXRpZXMKCkxldCdzIGFzc3VtZSBvdXIgdHJhaW5pbmcgZGF0YXNldCBoYXMgMTAwIGVtYWlscywgMzAgb2Ygd2hpY2ggYXJlIHNwYW0gYW5kIDcwIGFyZSBub3Qgc3BhbS4gVGhlIHByaW9yIHByb2JhYmlsaXRpZXMgYXJlOgoKJCQgUChcdGV4dHtzcGFtfSkgPSBcZnJhY3szMH17MTAwfSA9IDAuMyAkJCAkJCBQKFx0ZXh0e25vdCBzcGFtfSkgPSBcZnJhY3s3MH17MTAwfSA9IDAuNyAkJAoKIyMjIFN0ZXAgMjogQ2FsY3VsYXRlIExpa2VsaWhvb2RzCgpTdXBwb3NlIHdlIGFyZSBjb25zaWRlcmluZyB0d28gZmVhdHVyZXM6IHRoZSBwcmVzZW5jZSBvZiB0aGUgd29yZCAib2ZmZXIiIGFuZCB0aGUgd29yZCAiY2xpY2siLiBGcm9tIHRoZSB0cmFpbmluZyBkYXRhLCB3ZSBjYWxjdWxhdGUgdGhlIGxpa2VsaWhvb2RzOgoKJCQgUChcdGV4dHtvZmZlcn18XHRleHR7c3BhbX0pID0gXGZyYWN7XHRleHR7bnVtYmVyIG9mIHNwYW0gZW1haWxzIHdpdGggIm9mZmVyIn19e1x0ZXh0e3RvdGFsIG51bWJlciBvZiBzcGFtIGVtYWlsc319ICQkCgokJCBQKFx0ZXh0e2NsaWNrfXxcdGV4dHtzcGFtfSkgPSBcZnJhY3tcdGV4dHtudW1iZXIgb2Ygc3BhbSBlbWFpbHMgd2l0aCAiY2xpY2sifX17XHRleHR7dG90YWwgbnVtYmVyIG9mIHNwYW0gZW1haWxzfX0gJCQKClNpbWlsYXJseSwgZm9yIG5vbi1zcGFtIGVtYWlsczoKCiQkIFAoXHRleHR7b2ZmZXJ9fFx0ZXh0e25vdCBzcGFtfSkgPSBcZnJhY3tcdGV4dHtudW1iZXIgb2Ygbm90IHNwYW0gZW1haWxzIHdpdGggIm9mZmVyIn19e1x0ZXh0e3RvdGFsIG51bWJlciBvZiBub3Qgc3BhbSBlbWFpbHN9fSAkJAoKJCQgUChcdGV4dHtjbGlja318XHRleHR7bm90IHNwYW19KSA9IFxmcmFje1x0ZXh0e251bWJlciBvZiBub3Qgc3BhbSBlbWFpbHMgd2l0aCAiY2xpY2sifX17XHRleHR7dG90YWwgbnVtYmVyIG9mIG5vdCBzcGFtIGVtYWlsc319ICQkCgojIyMgU3RlcCAzOiBBcHBseSBCYXllcycgVGhlb3JlbQoKR2l2ZW4gYSBuZXcgZW1haWwgd2l0aCB0aGUgd29yZHMgIm9mZmVyIiBhbmQgImNsaWNrIiwgd2UgY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllcyBmb3IgZWFjaCBjbGFzcy4gRm9yIHNwYW06CgokJCBQKFx0ZXh0e3NwYW19fFx0ZXh0e29mZmVyfSwgXHRleHR7Y2xpY2t9KSA9IFxmcmFje1AoXHRleHR7b2ZmZXJ9fFx0ZXh0e3NwYW19KSBcY2RvdCBQKFx0ZXh0e2NsaWNrfXxcdGV4dHtzcGFtfSkgXGNkb3QgUChcdGV4dHtzcGFtfSl9e1AoXHRleHR7b2ZmZXJ9KSBcY2RvdCBQKFx0ZXh0e2NsaWNrfSl9ICQkCgpGb3Igbm90IHNwYW06CgokJCBQKFx0ZXh0e25vdCBzcGFtfXxcdGV4dHtvZmZlcn0sIFx0ZXh0e2NsaWNrfSkgPSBcZnJhY3tQKFx0ZXh0e29mZmVyfXxcdGV4dHtub3Qgc3BhbX0pIFxjZG90IFAoXHRleHR7Y2xpY2t9fFx0ZXh0e25vdCBzcGFtfSkgXGNkb3QgUChcdGV4dHtub3Qgc3BhbX0pfXtQKFx0ZXh0e29mZmVyfSkgXGNkb3QgUChcdGV4dHtjbGlja30pfSAkJAoKIyMjIFN0ZXAgNDogQ2xhc3MgUHJlZGljdGlvbgoKQ29tcGFyZSB0aGUgcG9zdGVyaW9yIHByb2JhYmlsaXRpZXMgYW5kIGNsYXNzaWZ5IHRoZSBlbWFpbCBhcyB0aGUgY2xhc3Mgd2l0aCB0aGUgaGlnaGVyIHByb2JhYmlsaXR5LgoKIyMgTWF0aGVtYXRpY2FsIERlZmluaXRpb24gb2YgTmFpdmUgQmF5ZXMKCkZvcm1hbGx5LCBnaXZlbiBhIGZlYXR1cmUgdmVjdG9yICRYID0gKHhfMSwgeF8yLCBcbGRvdHMsIHhfbikkIGFuZCBhIHNldCBvZiBjbGFzc2VzICRDID0gXHtjXzEsIGNfMiwgXGxkb3RzLCBjX2tcfSQsIHRoZSBjbGFzc2lmaWVyIGFzc2lnbnMgYSBjbGFzcyBsYWJlbCAkXGhhdHt5fSQgYWNjb3JkaW5nIHRvOgoKJCQgXGhhdHt5fSA9IFxhcmcgXG1heF97YyBcaW4gQ30gUChDPWMpIFxwcm9kX3tpPTF9XntufSBQKFhfaT14X2l8Qz1jKSAkJAoKVGhpcyBmb3JtdWxhdGlvbiBhc3N1bWVzIGNvbmRpdGlvbmFsIGluZGVwZW5kZW5jZSBvZiB0aGUgZmVhdHVyZXMgJHhfaSQuCgojIyMgUHJhY3RpY2FsIENvbnNpZGVyYXRpb25zCgoxLiAgKipMYXBsYWNlIFNtb290aGluZzoqKiBJbiBwcmFjdGljZSwgd2Ugb2Z0ZW4gdXNlIExhcGxhY2Ugc21vb3RoaW5nIHRvIGhhbmRsZSB0aGUgcHJvYmxlbSBvZiB6ZXJvIHByb2JhYmlsaXRpZXMuIFRoaXMgaW52b2x2ZXMgYWRkaW5nIGEgc21hbGwgdmFsdWUgKHR5cGljYWxseSAxKSB0byB0aGUgY291bnQgb2YgZWFjaCBmZWF0dXJlJ3Mgb2NjdXJyZW5jZXMuCjIuICAqKk11bHRpbm9taWFsIGFuZCBHYXVzc2lhbiBOYWl2ZSBCYXllczoqKiBUaGUgTmFpdmUgQmF5ZXMgY2xhc3NpZmllciBjYW4gYmUgYWRhcHRlZCB0byBkaWZmZXJlbnQgdHlwZXMgb2YgZGF0YS4gRm9yIGRpc2NyZXRlIGZlYXR1cmVzICgqZS5nLiosIHdvcmQgY291bnRzKSwgdGhlIE11bHRpbm9taWFsIE5haXZlIEJheWVzIGlzIHVzZWQuIEZvciBjb250aW51b3VzIGZlYXR1cmVzICgqZS5nLiosIHJlYWwtdmFsdWVkIG1lYXN1cmVtZW50cyksIHRoZSBHYXVzc2lhbiBOYWl2ZSBCYXllcyBpcyBtb3JlIGFwcHJvcHJpYXRlLCBhc3N1bWluZyBhIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgdGhlIGZlYXR1cmVzLgoKIyMgSW1wbGVtZW50YXRpb24gRXhhbXBsZSBpbiBSCgpIZXJlIGlzIGFuIGV4YW1wbGUgb2YgaW1wbGVtZW50aW5nIGEgTmFpdmUgQmF5ZXMgY2xhc3NpZmllciB1c2luZyB0aGUgYGUxMDcxYCBwYWNrYWdlIGluIFIgZm9yIGEgdGV4dCBjbGFzc2lmaWNhdGlvbiB0YXNrLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyeQpsaWJyYXJ5KGUxMDcxKQoKIyBTYW1wbGUgZGF0YQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgdGV4dCA9IGMoIm9mZmVyIGlzIHNlY3JldCIsICJjbGljayBzZWNyZXQgbGluayIsICJzZWNyZXQgc3BvcnRzIGxpbmsiLCAic3BvcnRzIGxpbmsgaXMgYXZhaWxhYmxlIiksCiAgY2xhc3MgPSBhcy5mYWN0b3IoYygic3BhbSIsICJzcGFtIiwgIm5vdF9zcGFtIiwgIm5vdF9zcGFtIikpCikKCiMgQ3JlYXRlIGEgRG9jdW1lbnQtVGVybSBNYXRyaXgKbGlicmFyeSh0bSkKY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UoZGF0YSR0ZXh0KSkKZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChjb3JwdXMpCmR0bSA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChkdG0pKQoKIyBDb21iaW5lIHRoZSBEVE0gd2l0aCB0aGUgY2xhc3MgbGFiZWxzCnRyYWluX2RhdGEgPC0gY2JpbmQoZHRtLCBjbGFzcyA9IGRhdGEkY2xhc3MpCgojIFRyYWluIHRoZSBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyCm1vZGVsIDwtIG5haXZlQmF5ZXMoY2xhc3MgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkKCiMgUHJlZGljdCBvbiBuZXcgZGF0YQpuZXdfdGV4dCA8LSBjKCJzcG9ydHMgaXMgc2VjcmV0IiwgIm9mZmVyIGxpbmsgYXZhaWxhYmxlIikKbmV3X2NvcnB1cyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKG5ld190ZXh0KSkKbmV3X2R0bSA8LSBEb2N1bWVudFRlcm1NYXRyaXgobmV3X2NvcnB1cykKbmV3X2R0bSA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChuZXdfZHRtKSkKcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3X2R0bSkKCnByaW50KHByZWRpY3Rpb25zKQpgYGAKClRoaXMgY29kZSBkZW1vbnN0cmF0ZXMgdGhlIHRyYWluaW5nIG9mIGEgTmFpdmUgQmF5ZXMgY2xhc3NpZmllciBvbiBhIHNpbXBsZSBkYXRhc2V0IGFuZCBob3cgdG8gdXNlIGl0IGZvciBwcmVkaWN0aW9uLiBUaGUgYGUxMDcxYCBwYWNrYWdlIHByb3ZpZGVzIGEgc3RyYWlnaHRmb3J3YXJkIGludGVyZmFjZSBmb3IgY3JlYXRpbmcgYW5kIHVzaW5nIE5haXZlIEJheWVzIGNsYXNzaWZpZXJzIGluIFIuCgojIyBDb21wdXRhdGlvbmFsIEFzc3VtcHRpb25zCgpXaXRob3V0IHRoZSBhc3N1bXB0aW9uIG9mIGNvbmRpdGlvbmFsIGluZGVwZW5kZW5jZSwgdGhlIGNvbXB1dGF0aW9uIG9mIHRoZSBqb2ludCBwcm9iYWJpbGl0eSAkUChYfEMpJCB3b3VsZCBiZSBleHRyZW1lbHkgY29tcGxleC4gRm9yIGEgZmVhdHVyZSB2ZWN0b3IgJFggPSAoeF8xLCB4XzIsIFxsZG90cywgeF9uKSQsIHRoZSBqb2ludCBwcm9iYWJpbGl0eSB3aXRob3V0IHRoZSBpbmRlcGVuZGVuY2UgYXNzdW1wdGlvbiB3b3VsZCByZXF1aXJlIG1vZGVsaW5nIHRoZSBmdWxsIGpvaW50IGRpc3RyaWJ1dGlvbiBvZiB0aGUgZmVhdHVyZXMgZ2l2ZW4gdGhlIGNsYXNzOgoKJCQgUChYfEMpID0gUCh4XzEsIHhfMiwgXGxkb3RzLCB4X258QykgJCQKClRoaXMgcmVxdWlyZXMgZXN0aW1hdGluZyB0aGUgcHJvYmFiaWxpdGllcyBvZiBhbGwgcG9zc2libGUgY29tYmluYXRpb25zIG9mIGZlYXR1cmUgdmFsdWVzIGZvciBlYWNoIGNsYXNzLCB3aGljaCBpcyBjb21wdXRhdGlvbmFsbHkgdW5mZWFzaWJsZSBmb3IgbGFyZ2UgJG4kLiBUaGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgcmVxdWlyZWQgd291bGQgZ3JvdyBleHBvbmVudGlhbGx5IHdpdGggdGhlIG51bWJlciBvZiBmZWF0dXJlcywgbWFraW5nIHRoZSBtb2RlbCBwcm9uZSB0byBvdmVyZml0dGluZywgZXNwZWNpYWxseSB3aXRoIGxpbWl0ZWQgdHJhaW5pbmcgZGF0YS4KCkJ5IGFzc3VtaW5nIGNvbmRpdGlvbmFsIGluZGVwZW5kZW5jZSwgdGhlIGpvaW50IHByb2JhYmlsaXR5IHNpbXBsaWZpZXMgdG8gdGhlIHByb2R1Y3Qgb2YgaW5kaXZpZHVhbCBwcm9iYWJpbGl0aWVzOgoKJCQgUChYfEMpID0gXHByb2Rfe2k9MX1ee259IFAoeF9pfEMpICQkCgpUaGlzIHNpbXBsaWZpY2F0aW9uIGRyYXN0aWNhbGx5IHJlZHVjZXMgdGhlIG51bWJlciBvZiBwYXJhbWV0ZXJzIHRvIGVzdGltYXRlLCBtYWtpbmcgdGhlIG1vZGVsIGJvdGggY29tcHV0YXRpb25hbGx5IGVmZmljaWVudCBhbmQgc2NhbGFibGUgdG8gaGlnaC1kaW1lbnNpb25hbCBkYXRhLiBFYWNoIGZlYXR1cmUgY2FuIGJlIHRyZWF0ZWQgaW5kZXBlbmRlbnRseSwgYW5kIHRoZSBvdmVyYWxsIGxpa2VsaWhvb2QgaXMgYSBzaW1wbGUgcHJvZHVjdCBvZiBpbmRpdmlkdWFsIGxpa2VsaWhvb2RzLgoKIyMjIEZlYXNpYmlsaXR5IG9mIFBhcmFtZXRlciBFc3RpbWF0aW9uCgpJbiBwcmFjdGljYWwgdGVybXMsIHRoZSBhbW91bnQgb2YgZGF0YSByZXF1aXJlZCB0byByZWxpYWJseSBlc3RpbWF0ZSB0aGUgam9pbnQgZGlzdHJpYnV0aW9uIG9mIGFsbCBmZWF0dXJlcyB3b3VsZCBiZSBwcm9oaWJpdGl2ZWx5IGxhcmdlIGlmIHRoZSBmZWF0dXJlcyB3ZXJlIG5vdCBhc3N1bWVkIHRvIGJlIGluZGVwZW5kZW50LiBGb3IgaW5zdGFuY2UsIGlmIGVhY2ggZmVhdHVyZSBjYW4gdGFrZSAkbSQgcG9zc2libGUgdmFsdWVzLCB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgcmVxdWlyZWQgdG8gZGVzY3JpYmUgdGhlIGpvaW50IGRpc3RyaWJ1dGlvbiBvZiAkbiQgZmVhdHVyZXMgaXMgJG1ebiQuIEluIGNvbnRyYXN0LCB1bmRlciB0aGUgY29uZGl0aW9uYWwgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24sIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBpcyByZWR1Y2VkIHRvICRuIFxjZG90IG0kLCB3aGljaCBpcyBsaW5lYXIgaW4gdGhlIG51bWJlciBvZiBmZWF0dXJlcy4KCiMjIyBDb21wdXRhdGlvbmFsIEVmZmljaWVuY3kKClRoZSBpbmRlcGVuZGVuY2UgYXNzdW1wdGlvbiBhbGxvd3MgdGhlIE5haXZlIEJheWVzIGNsYXNzaWZpZXIgdG8gYmUgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudCBib3RoIGluIHRlcm1zIG9mIHRyYWluaW5nIGFuZCBpbmZlcmVuY2UuIFRoZSB0cmFpbmluZyBwcm9jZXNzIGludm9sdmVzIGVzdGltYXRpbmcgdGhlIHByaW9yIHByb2JhYmlsaXRpZXMgJFAoQykkIGFuZCB0aGUgY29uZGl0aW9uYWwgcHJvYmFiaWxpdGllcyAkUCh4X2l8QykkLCB3aGljaCBjYW4gYmUgZG9uZSB3aXRoIHNpbXBsZSBjb3VudGluZyBhbmQgbm9ybWFsaXphdGlvbi4gVGhpcyBtYWtlcyB0aGUgdHJhaW5pbmcgcGhhc2UgdmVyeSBmYXN0IGNvbXBhcmVkIHRvIG1vcmUgY29tcGxleCBtb2RlbHMgdGhhdCByZXF1aXJlIGl0ZXJhdGl2ZSBvcHRpbWl6YXRpb24gdGVjaG5pcXVlcy4KCkR1cmluZyBpbmZlcmVuY2UsIGNhbGN1bGF0aW5nIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZm9yIGEgZ2l2ZW4gaW5zdGFuY2UgaW52b2x2ZXMgbXVsdGlwbHlpbmcgdGhlIHByb2JhYmlsaXRpZXMgb2YgaW5kaXZpZHVhbCBmZWF0dXJlcywgd2hpY2ggaXMgY29tcHV0YXRpb25hbGx5IHZlcnkgaW5leHBlbnNpdmUuIFRoaXMgbWFrZXMgTmFpdmUgQmF5ZXMgY2xhc3NpZmllcnMgcGFydGljdWxhcmx5IHN1aXRhYmxlIGZvciByZWFsLXRpbWUgYXBwbGljYXRpb25zIHdoZXJlIHF1aWNrIGRlY2lzaW9uLW1ha2luZyBpcyBlc3NlbnRpYWwgYW5kIGRlc2lyYWJsZS4KCiMjIyBSb2J1c3RuZXNzIGFuZCBFZmZlY3RpdmVuZXNzCgpEZXNwaXRlIHRoZSBvZnRlbiB1bnJlYWxpc3RpYyBhc3N1bXB0aW9uIG9mIGZlYXR1cmUgaW5kZXBlbmRlbmNlLCBOYWl2ZSBCYXllcyBjbGFzc2lmaWVycyBoYXZlIGJlZW4gZm91bmQgdG8gcGVyZm9ybSBzdXJwcmlzaW5nbHkgd2VsbCBpbiBwcmFjdGljZSwgZXNwZWNpYWxseSBpbiBkb21haW5zIHN1Y2ggYXMgdGV4dCBjbGFzc2lmaWNhdGlvbiBhbmQgc3BhbSBmaWx0ZXJpbmcuIFRoaXMgY2FuIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIGZhY3QgdGhhdCB0aGUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24gb2Z0ZW4gaG9sZHMgd2VsbCBlbm91Z2ggdG8gbWFrZSB0aGUgbW9kZWwgZWZmZWN0aXZlLCBldmVuIGlmIG5vdCBlbnRpcmVseSBhY2N1cmF0ZS4gVGhlIGVycm9ycyBpbnRyb2R1Y2VkIGJ5IHRoZSBpbmRlcGVuZGVuY2UgYXNzdW1wdGlvbiB0ZW5kIHRvIGNhbmNlbCBvdXQgb3ZlciB0aGUgbWFueSBmZWF0dXJlcywgbGVhZGluZyB0byBnb29kIG92ZXJhbGwgcGVyZm9ybWFuY2UuCgojIyMgRXhhbXBsZTogRG9jdW1lbnQgQ2xhc3NpZmljYXRpb24KCkNvbnNpZGVyIHRoZSB0YXNrIG9mIGNsYXNzaWZ5aW5nIGRvY3VtZW50cyBiYXNlZCBvbiB0aGUgcHJlc2VuY2Ugb2YgY2VydGFpbiB3b3Jkcy4gSWYgd2UgZGlkIG5vdCBhc3N1bWUgaW5kZXBlbmRlbmNlLCB3ZSB3b3VsZCBuZWVkIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBvZiBldmVyeSBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiB3b3JkcyBhcHBlYXJpbmcgdG9nZXRoZXIgaW4gYSBkb2N1bWVudCBmb3IgZWFjaCBjbGFzcy4gVGhpcyBpcyBub3QgZmVhc2libGUgZHVlIHRvIHRoZSBlbm9ybW91cyBudW1iZXIgb2YgY29tYmluYXRpb25zLCBlc3BlY2lhbGx5IHdpdGggYSBsYXJnZSB2b2NhYnVsYXJ5LgoKV2l0aCB0aGUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24sIHdlIG9ubHkgbmVlZCB0byBlc3RpbWF0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgZWFjaCB3b3JkIGFwcGVhcmluZyBpbiBhIGRvY3VtZW50IGdpdmVuIHRoZSBjbGFzcy4gVGhpcyBjYW4gYmUgZG9uZSBieSBzaW1wbHkgY291bnRpbmcgdGhlIG9jY3VycmVuY2VzIG9mIGVhY2ggd29yZCBpbiBkb2N1bWVudHMgb2YgZWFjaCBjbGFzcy4gRm9yIGV4YW1wbGUsIGluIHNwYW0gZW1haWwgY2xhc3NpZmljYXRpb24sIHdlIGFzc3VtZSB0aGF0IHRoZSBwcmVzZW5jZSBvZiB0aGUgd29yZCAib2ZmZXIiIGlzIGluZGVwZW5kZW50IG9mIHRoZSBwcmVzZW5jZSBvZiB0aGUgd29yZCAiY2xpY2siIGdpdmVuIHRoZSBlbWFpbCBpcyBzcGFtLiBUaGlzIGFsbG93cyB1cyB0byBjb21wdXRlOgoKJCQgUChcdGV4dHtvZmZlciwgY2xpY2t9fFx0ZXh0e3NwYW19KSA9IFAoXHRleHR7b2ZmZXJ9fFx0ZXh0e3NwYW19KSBcY2RvdCBQKFx0ZXh0e2NsaWNrfXxcdGV4dHtzcGFtfSkgJCQKClRoaXMgc2ltcGxpZmljYXRpb24gbWFrZXMgaXQgZmVhc2libGUgdG8gaGFuZGxlIHRoZSBoaWdoIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBmZWF0dXJlIHNwYWNlICgqaS5lLiosIHRoZSBsYXJnZSBudW1iZXIgb2YgdW5pcXVlIHdvcmRzIGluIHRoZSB2b2NhYnVsYXJ5KSB3aXRob3V0IHJlcXVpcmluZyBhbiBpbXByYWN0aWNhbCBhbW91bnQgb2YgdHJhaW5pbmcgZGF0YS4KCiMjIENvbW1vbiBVc2UgQ2FzZXMgZm9yIE5haXZlIEJheWVzIENsYXNzaWZpZXIKClRoZSBOYWl2ZSBCYXllcyBhbGdvcml0aG0gaXMgd2lkZWx5IHVzZWQgaW4gdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIGFwcGxpY2F0aW9ucyBkdWUgdG8gaXRzIHNpbXBsaWNpdHksIGVmZmljaWVuY3ksIGFuZCBlZmZlY3RpdmVuZXNzLCBlc3BlY2lhbGx5IGluIHNjZW5hcmlvcyB3aGVyZSB0aGUgYXNzdW1wdGlvbiBvZiBmZWF0dXJlIGluZGVwZW5kZW5jZSBpcyByZWFzb25hYmx5IHZhbGlkLiBIZXJlIGFyZSBzb21lIGNvbW1vbiB1c2UgY2FzZXM6CgojIyMgMS4gVGV4dCBDbGFzc2lmaWNhdGlvbgoKIyMjIyBTcGFtIERldGVjdGlvbgoKT25lIG9mIHRoZSBtb3N0IHdlbGwta25vd24gYXBwbGljYXRpb25zIG9mIE5haXZlIEJheWVzIGlzIGluIHNwYW0gZmlsdGVyaW5nLiBFbWFpbCBzZXJ2aWNlcyB1c2UgTmFpdmUgQmF5ZXMgY2xhc3NpZmllcnMgdG8gaWRlbnRpZnkgc3BhbSBlbWFpbHMgYmFzZWQgb24gdGhlIG9jY3VycmVuY2Ugb2Ygc3BlY2lmaWMgd29yZHMgYW5kIHBocmFzZXMgdGhhdCBhcmUgY29tbW9uIGluIHNwYW0uCgojIyMjIFNlbnRpbWVudCBBbmFseXNpcwoKTmFpdmUgQmF5ZXMgaXMgb2Z0ZW4gdXNlZCB0byBjbGFzc2lmeSB0aGUgc2VudGltZW50IG9mIHRleHQsIHN1Y2ggYXMgZGV0ZXJtaW5pbmcgd2hldGhlciBhIHByb2R1Y3QgcmV2aWV3IG9yIHNvY2lhbCBtZWRpYSBwb3N0IGlzIHBvc2l0aXZlLCBuZWdhdGl2ZSwgb3IgbmV1dHJhbC4gVGhpcyBpbnZvbHZlcyBhbmFseXppbmcgdGhlIGZyZXF1ZW5jeSBvZiB3b3JkcyBhc3NvY2lhdGVkIHdpdGggZGlmZmVyZW50IHNlbnRpbWVudHMuCgojIyMjIERvY3VtZW50IENhdGVnb3JpemF0aW9uCgpJbiBuZXdzIGFnZ3JlZ2F0aW9uIGFuZCBjb250ZW50IG1hbmFnZW1lbnQgc3lzdGVtcywgTmFpdmUgQmF5ZXMgY2xhc3NpZmllcnMgYXJlIHVzZWQgdG8gY2F0ZWdvcml6ZSBkb2N1bWVudHMgaW50byBwcmVkZWZpbmVkIGNhdGVnb3JpZXMgc3VjaCBhcyBzcG9ydHMsIHBvbGl0aWNzLCB0ZWNobm9sb2d5LCBhbmQgZW50ZXJ0YWlubWVudCBiYXNlZCBvbiB0aGUgdGV4dCBjb250ZW50LgoKIyMjIDIuIE1lZGljYWwgRGlhZ25vc2lzCgpOYWl2ZSBCYXllcyBjbGFzc2lmaWVycyBjYW4gYXNzaXN0IGluIGRpYWdub3NpbmcgZGlzZWFzZXMgYnkgYW5hbHl6aW5nIHBhdGllbnQgZGF0YSwgaW5jbHVkaW5nIHN5bXB0b21zIGFuZCB0ZXN0IHJlc3VsdHMuIEdpdmVuIHRoZSBwcmVzZW5jZSBvZiBjZXJ0YWluIHN5bXB0b21zLCB0aGUgYWxnb3JpdGhtIGNhbiBwcmVkaWN0IHRoZSBsaWtlbGlob29kIG9mIHZhcmlvdXMgZGlzZWFzZXMuIFRoaXMgY2FuIGJlIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gc2NlbmFyaW9zIHdoZXJlIGRpZmZlcmVudCBzeW1wdG9tcyBhcmUgY29uc2lkZXJlZCBpbmRlcGVuZGVudCBnaXZlbiB0aGUgZGlzZWFzZS4KCiMjIyAzLiBJbWFnZSBSZWNvZ25pdGlvbgoKV2hpbGUgTmFpdmUgQmF5ZXMgaXMgbm90IHRoZSBtb3N0IGNvbW1vbiBhbGdvcml0aG0gZm9yIGltYWdlIHJlY29nbml0aW9uLCBpdCBjYW4gYmUgYXBwbGllZCB0byBzcGVjaWZpYyB0YXNrcyB3aGVyZSBmZWF0dXJlcyBleHRyYWN0ZWQgZnJvbSBpbWFnZXMgKHN1Y2ggYXMgcGl4ZWwgdmFsdWVzIG9yIG1vcmUgYWJzdHJhY3QgZmVhdHVyZXMpIGFyZSB1c2VkIHRvIGNsYXNzaWZ5IGltYWdlcy4gRm9yIGluc3RhbmNlLCBpdCBjYW4gYmUgdXNlZCBpbiBoYW5kd3JpdGluZyByZWNvZ25pdGlvbiB0byBjbGFzc2lmeSBkaWdpdHMgaW4gc2Nhbm5lZCBkb2N1bWVudHMuCgojIyMgNC4gUmVjb21tZW5kYXRpb24gU3lzdGVtcwoKTmFpdmUgQmF5ZXMgY2FuIGJlIHVzZWQgdG8gYnVpbGQgcmVjb21tZW5kYXRpb24gc3lzdGVtcyB0aGF0IHN1Z2dlc3QgcHJvZHVjdHMsIG1vdmllcywgb3Igb3RoZXIgaXRlbXMgdG8gdXNlcnMgYmFzZWQgb24gdGhlaXIgcGFzdCBiZWhhdmlvciBhbmQgcHJlZmVyZW5jZXMuIEZvciBleGFtcGxlLCBpbiBhIG1vdmllIHJlY29tbWVuZGF0aW9uIHN5c3RlbSwgdGhlIGFsZ29yaXRobSBjYW4gY2xhc3NpZnkgbW92aWVzIGludG8gZ2VucmVzIHRoYXQgYSB1c2VyIGlzIGxpa2VseSB0byBlbmpveSBiYXNlZCBvbiB0aGVpciB2aWV3aW5nIGhpc3RvcnkuCgojIyMgNS4gUmVhbC10aW1lIFByZWRpY3Rpb24KCkR1ZSB0byBpdHMgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LCBOYWl2ZSBCYXllcyBpcyBzdWl0YWJsZSBmb3IgcmVhbC10aW1lIHByZWRpY3Rpb24gdGFza3MuIEFwcGxpY2F0aW9ucyBpbmNsdWRlIHJlYWwtdGltZSBmcmF1ZCBkZXRlY3Rpb24gaW4gZmluYW5jaWFsIHRyYW5zYWN0aW9ucywgd2hlcmUgdGhlIGFsZ29yaXRobSBjYW4gcXVpY2tseSBjbGFzc2lmeSB0cmFuc2FjdGlvbnMgYXMgZnJhdWR1bGVudCBvciBsZWdpdGltYXRlIGJhc2VkIG9uIHZhcmlvdXMgZmVhdHVyZXMuCgojIyMgNi4gQW5vbWFseSBEZXRlY3Rpb24KCk5haXZlIEJheWVzIGNhbiBiZSB1c2VkIGZvciBhbm9tYWx5IGRldGVjdGlvbiBpbiB2YXJpb3VzIGRvbWFpbnMgc3VjaCBhcyBuZXR3b3JrIHNlY3VyaXR5LCB3aGVyZSBpdCBjYW4gY2xhc3NpZnkgbmV0d29yayBhY3Rpdml0aWVzIGFzIG5vcm1hbCBvciBzdXNwaWNpb3VzIGJhc2VkIG9uIGZlYXR1cmVzIGxpa2UgSVAgYWRkcmVzc2VzLCBwb3J0IG51bWJlcnMsIGFuZCBwYWNrZXQgc2l6ZXMuCgojIyMgNy4gUmVjb21tZW5kYXRpb24gU3lzdGVtcwoKSW4gY29sbGFib3JhdGl2ZSBmaWx0ZXJpbmcsIE5haXZlIEJheWVzIGNhbiBiZSB1c2VkIHRvIHJlY29tbWVuZCBwcm9kdWN0cyB0byB1c2VycyBiYXNlZCBvbiB0aGVpciBwcmVmZXJlbmNlcyBhbmQgYmVoYXZpb3JzLiBGb3IgZXhhbXBsZSwgaW4gYW4gZS1jb21tZXJjZSBzZXR0aW5nLCBpdCBjYW4gc3VnZ2VzdCBwcm9kdWN0cyB0byB1c2VycyBiYXNlZCBvbiB0aGVpciBwcmV2aW91cyBwdXJjaGFzZXMgYW5kIGJyb3dzaW5nIGhpc3RvcnkuCgojIyMgOC4gTGFuZ3VhZ2UgUHJvY2Vzc2luZwoKTmFpdmUgQmF5ZXMgY2xhc3NpZmllcnMgYXJlIHVzZWQgaW4gdmFyaW91cyBuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcgKE5MUCkgdGFza3Mgc3VjaCBhcyBsYW5ndWFnZSBpZGVudGlmaWNhdGlvbiwgd2hlcmUgdGhlIGFsZ29yaXRobSBkZXRlcm1pbmVzIHRoZSBsYW5ndWFnZSBvZiBhIGdpdmVuIHRleHQsIGFuZCBwYXJ0LW9mLXNwZWVjaCB0YWdnaW5nLCB3aGVyZSBpdCBhc3NpZ25zIHBhcnRzIG9mIHNwZWVjaCB0byB3b3JkcyBpbiBhIHNlbnRlbmNlLgoKIyMjIDkuIEN1c3RvbWVyIFJlbGF0aW9uc2hpcCBNYW5hZ2VtZW50IChDUk0pCgpJbiBDUk0gc3lzdGVtcywgTmFpdmUgQmF5ZXMgY2FuIGJlIHVzZWQgdG8gY2xhc3NpZnkgY3VzdG9tZXIgZmVlZGJhY2ssIHNlZ21lbnQgY3VzdG9tZXJzIGJhc2VkIG9uIHRoZWlyIGJlaGF2aW9yLCBhbmQgcHJlZGljdCBjdXN0b21lciBjaHVybiwgaGVscGluZyBidXNpbmVzc2VzIHRvIGRldmlzZSB0YXJnZXRlZCBtYXJrZXRpbmcgc3RyYXRlZ2llcyBhbmQgaW1wcm92ZSBjdXN0b21lciByZXRlbnRpb24uCgojIyMgRXhhbXBsZTogU3BhbSBEZXRlY3Rpb24KClRvIGlsbHVzdHJhdGUsIGxldCdzIGNvbnNpZGVyIGEgc2ltcGxlIGV4YW1wbGUgb2Ygc3BhbSBkZXRlY3Rpb24uIFN1cHBvc2Ugd2UgaGF2ZSBhIGRhdGFzZXQgb2YgZW1haWxzIGxhYmVsZWQgYXMgc3BhbSBvciBub3Qgc3BhbSwgYW5kIHdlIHdhbnQgdG8gYnVpbGQgYSBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyIHRvIGF1dG9tYXRpY2FsbHkgY2xhc3NpZnkgbmV3IGVtYWlscy4KCiMjIyMgVHJhaW5pbmcgRGF0YQoKfCBFbWFpbCBUZXh0ICAgICAgICAgICAgICAgICAgIHwgTGFiZWwgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS18CnwgIldpbiBtb25leSBub3ciICAgICAgICAgICAgICB8IFNwYW0gICAgIHwKfCAiSGVsbG8sIGhvdyBhcmUgeW91PyIgICAgICAgIHwgTm90IFNwYW0gfAp8ICJTcGVjaWFsIG9mZmVyIGp1c3QgZm9yIHlvdSIgfCBTcGFtICAgICB8CnwgIk1lZXRpbmcgdG9tb3Jyb3ciICAgICAgICAgICB8IE5vdCBTcGFtIHwKCiMjIyMgU3RlcC1ieS1TdGVwIFByb2Nlc3MKCjEuICAqKkNhbGN1bGF0ZSBQcmlvciBQcm9iYWJpbGl0aWVzOioqCiAgICAtICAgJFAoXHRleHR7U3BhbX0pID0gXGZyYWN7Mn17NH0gPSAwLjUkCiAgICAtICAgJFAoXHRleHR7Tm90IFNwYW19KSA9IFxmcmFjezJ9ezR9ID0gMC41JAoyLiAgKipDYWxjdWxhdGUgTGlrZWxpaG9vZHM6KioKICAgIC0gICAkUChcdGV4dHtXaW59fFx0ZXh0e1NwYW19KSA9IFxmcmFjezF9ezJ9JAogICAgLSAgICRQKFx0ZXh0e21vbmV5fXxcdGV4dHtTcGFtfSkgPSBcZnJhY3sxfXsyfSQKICAgIC0gICAkUChcdGV4dHtub3d9fFx0ZXh0e1NwYW19KSA9IFxmcmFjezF9ezJ9JAogICAgLSAgICRQKFx0ZXh0e0hlbGxvfXxcdGV4dHtOb3QgU3BhbX0pID0gXGZyYWN7MX17Mn0kCiAgICAtICAgJFAoXHRleHR7YXJlfXxcdGV4dHtOb3QgU3BhbX0pID0gXGZyYWN7MX17Mn0kCiAgICAtICAgJFAoXHRleHR7eW91fXxcdGV4dHtOb3QgU3BhbX0pID0gXGZyYWN7MX17Mn0kCjMuICAqKkNsYXNzaWZ5IE5ldyBFbWFpbDoqKgogICAgLSAgIEZvciBhIG5ldyBlbWFpbCAiV2luIG1vbmV5IjogJCQgUChcdGV4dHtTcGFtfXxcdGV4dHtXaW4gbW9uZXl9KSA9IFAoXHRleHR7U3BhbX0pIFxjZG90IFAoXHRleHR7V2lufXxcdGV4dHtTcGFtfSkgXGNkb3QgUChcdGV4dHttb25leX18XHRleHR7U3BhbX0pID0gMC41IFxjZG90IDAuNSBcY2RvdCAwLjUgPSAwLjEyNSAkJCAkJCBQKFx0ZXh0e05vdCBTcGFtfXxcdGV4dHtXaW4gbW9uZXl9KSA9IFAoXHRleHR7Tm90IFNwYW19KSBcY2RvdCBQKFx0ZXh0e1dpbn18XHRleHR7Tm90IFNwYW19KSBcY2RvdCBQKFx0ZXh0e21vbmV5fXxcdGV4dHtOb3QgU3BhbX0pID0gMC41IFxjZG90IDAgXGNkb3QgMCA9IDAgJCQKICAgIC0gICBUaGUgZW1haWwgIldpbiBtb25leSIgd291bGQgYmUgY2xhc3NpZmllZCBhcyBTcGFtLgoKIyMjIEV4YW1wbGU6IE1lZGljYWwgRGlhZ25vc2lzCgpMZXQncyBjb25zaWRlciBhbiBleGFtcGxlIG9mIHVzaW5nIHRoZSBOYWl2ZSBCYXllcyBDbGFzc2lmaWVyIGZvciBkaWFnbm9zaW5nIGEgbWVkaWNhbCBjb25kaXRpb24sIHN1Y2ggYXMgaGVhcnQgZGlzZWFzZS4gSW4gdGhpcyBzY2VuYXJpbywgdGhlIGNsYXNzaWZpZXIgd2lsbCBwcmVkaWN0IHdoZXRoZXIgYSBwYXRpZW50IGhhcyBoZWFydCBkaXNlYXNlIGJhc2VkIG9uIHNldmVyYWwgZmVhdHVyZXMgc3VjaCBhcyBhZ2UsIGNob2xlc3Rlcm9sIGxldmVsLCBibG9vZCBwcmVzc3VyZSwgYW5kIHByZXNlbmNlIG9mIGNlcnRhaW4gc3ltcHRvbXMuCgojIyMjIERhdGFzZXQKClN1cHBvc2Ugd2UgaGF2ZSBhIGRhdGFzZXQgd2l0aCB0aGUgZm9sbG93aW5nIGZlYXR1cmVzIGZvciBlYWNoIHBhdGllbnQ6CgotICAgQWdlCi0gICBDaG9sZXN0ZXJvbCBsZXZlbAotICAgQmxvb2QgcHJlc3N1cmUKLSAgIFByZXNlbmNlIG9mIGNoZXN0IHBhaW4KLSAgIEV4ZXJjaXNlLWluZHVjZWQgYW5naW5hCgpFYWNoIHBhdGllbnQgaXMgYWxzbyBsYWJlbGVkIGFzIGVpdGhlciBoYXZpbmcgaGVhcnQgZGlzZWFzZSAoeWVzKSBvciBub3QgaGF2aW5nIGhlYXJ0IGRpc2Vhc2UgKG5vKS4KCiMjIyMgU3RlcC1ieS1TdGVwIFByb2Nlc3MKCjEuICAqKkNhbGN1bGF0ZSBQcmlvciBQcm9iYWJpbGl0aWVzOioqCgpMZXQncyBhc3N1bWUgb3VyIHRyYWluaW5nIGRhdGFzZXQgaGFzIDEwMCBwYXRpZW50cywgNDAgb2Ygd2hvbSBoYXZlIGhlYXJ0IGRpc2Vhc2UuIFRoZSBwcmlvciBwcm9iYWJpbGl0aWVzIHRoZXJlZm9yZSBhcmU6CgokJCBQKFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7eWVzfSkgPSBcZnJhY3s0MH17MTAwfSA9IDAuNCAkJAoKJCQgUChcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e25vfSkgPSBcZnJhY3s2MH17MTAwfSA9IDAuNiAkJAoKMi4gICoqQ2FsY3VsYXRlIExpa2VsaWhvb2RzOioqCgpXZSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgbGlrZWxpaG9vZCBvZiBlYWNoIGZlYXR1cmUgZ2l2ZW4gdGhlIGNsYXNzLiBGb3Igc2ltcGxpY2l0eSwgbGV0J3MgYXNzdW1lIG91ciBmZWF0dXJlcyBhcmUgY2F0ZWdvcmljYWwgKCplLmcuKiwgImhpZ2giIG9yICJub3JtYWwiIGZvciBjaG9sZXN0ZXJvbCwgInllcyIgb3IgIm5vIiBmb3IgY2hlc3QgcGFpbikuCgpGb3IgZXhhbXBsZSwgd2UgY2FsY3VsYXRlIHRoZSBsaWtlbGlob29kcyBmb3IgY2hvbGVzdGVyb2wgbGV2ZWxzOgoKJCQgUChcdGV4dHtDaG9sZXN0ZXJvbH0gPSBcdGV4dHtoaWdofXxcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e3llc30pID0gXGZyYWN7XHRleHR7bnVtYmVyIG9mIHBhdGllbnRzIHdpdGggaGlnaCBjaG9sZXN0ZXJvbCBhbmQgaGVhcnQgZGlzZWFzZX19e1x0ZXh0e251bWJlciBvZiBwYXRpZW50cyB3aXRoIGhlYXJ0IGRpc2Vhc2V9fSAkJAoKJCQgUChcdGV4dHtDaG9sZXN0ZXJvbH0gPSBcdGV4dHtub3JtYWx9fFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7eWVzfSkgPSBcZnJhY3tcdGV4dHtudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBub3JtYWwgY2hvbGVzdGVyb2wgYW5kIGhlYXJ0IGRpc2Vhc2V9fXtcdGV4dHtudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBoZWFydCBkaXNlYXNlfX0gJCQKCkFuZCBzaW1pbGFybHkgZm9yIHBhdGllbnRzIHdpdGhvdXQgaGVhcnQgZGlzZWFzZToKCiQkIFAoXHRleHR7Q2hvbGVzdGVyb2x9ID0gXHRleHR7aGlnaH18XHRleHR7SGVhcnQgRGlzZWFzZX0gPSBcdGV4dHtub30pID0gXGZyYWN7XHRleHR7bnVtYmVyIG9mIHBhdGllbnRzIHdpdGggaGlnaCBjaG9sZXN0ZXJvbCBhbmQgbm8gaGVhcnQgZGlzZWFzZX19e1x0ZXh0e251bWJlciBvZiBwYXRpZW50cyB3aXRoIG5vIGhlYXJ0IGRpc2Vhc2V9fSAkJAoKJCQgUChcdGV4dHtDaG9sZXN0ZXJvbH0gPSBcdGV4dHtub3JtYWx9fFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7bm99KSA9IFxmcmFje1x0ZXh0e251bWJlciBvZiBwYXRpZW50cyB3aXRoIG5vcm1hbCBjaG9sZXN0ZXJvbCBhbmQgbm8gaGVhcnQgZGlzZWFzZX19e1x0ZXh0e251bWJlciBvZiBwYXRpZW50cyB3aXRoIG5vIGhlYXJ0IGRpc2Vhc2V9fSAkJAoKTGV0J3MgYXNzdW1lIHRoZSBmb2xsb3dpbmcgY2FsY3VsYXRlZCBsaWtlbGlob29kcyBiYXNlZCBvbiBvdXIgZGF0YXNldDoKCi0gICAkUChcdGV4dHtDaG9sZXN0ZXJvbH0gPSBcdGV4dHtoaWdofXxcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e3llc30pID0gMC43JAotICAgJFAoXHRleHR7Q2hvbGVzdGVyb2x9ID0gXHRleHR7bm9ybWFsfXxcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e3llc30pID0gMC4zJAotICAgJFAoXHRleHR7Q2hvbGVzdGVyb2x9ID0gXHRleHR7aGlnaH18XHRleHR7SGVhcnQgRGlzZWFzZX0gPSBcdGV4dHtub30pID0gMC40JAotICAgJFAoXHRleHR7Q2hvbGVzdGVyb2x9ID0gXHRleHR7bm9ybWFsfXxcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e25vfSkgPSAwLjYkCgpXZSB3b3VsZCByZXBlYXQgdGhpcyBwcm9jZXNzIGZvciBhbGwgb2YgdGhlIG90aGVyIGZlYXR1cmVzIHN1Y2ggYXMgYWdlLCBibG9vZCBwcmVzc3VyZSwgY2hlc3QgcGFpbiwgYW5kIGV4ZXJjaXNlLWluZHVjZWQgYW5naW5hLgoKMy4gICoqQ2xhc3NpZnkgTmV3IFBhdGllbnQ6KioKCkdpdmVuIGEgbmV3IHBhdGllbnQgd2l0aCB0aGUgZm9sbG93aW5nIGNoYXJhY3RlcmlzdGljczoKCi0gICBBZ2U6IGFib3ZlIDUwCgotICAgQ2hvbGVzdGVyb2wgbGV2ZWw6IGhpZ2gKCi0gICBCbG9vZCBwcmVzc3VyZTogaGlnaAoKLSAgIENoZXN0IHBhaW46IHllcwoKLSAgIEV4ZXJjaXNlLWluZHVjZWQgYW5naW5hOiB5ZXMKCiAgICBXZSBjYWxjdWxhdGUgdGhlIHBvc3RlcmlvciBwcm9iYWJpbGl0eSBmb3IgYm90aCBjbGFzc2VzIChoZWFydCBkaXNlYXNlOiB5ZXMgb3Igbm8pLgoKICAgIEZvciBoZWFydCBkaXNlYXNlICh5ZXMpOgoKICAgICQkCiAgICBQKFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7eWVzfXxcdGV4dHtmZWF0dXJlc30pID0gUChcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e3llc30pIFx0aW1lcyBQKFx0ZXh0e0FnZX0gPiA1MHxcdGV4dHtIZWFydCBEaXNlYXNlfSA9IFx0ZXh0e3llc30pIFx0aW1lcyBQKFx0ZXh0e0Nob2xlc3Rlcm9sfSA9IFx0ZXh0e2hpZ2h9fFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7eWVzfSkgXHRpbWVzIFxsZG90cwogICAgJCQKCiAgICBGb3IgaGVhcnQgZGlzZWFzZSAobm8pOgoKICAgICQkCiAgICBQKFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7bm99fFx0ZXh0e2ZlYXR1cmVzfSkgPSBQKFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7bm99KSBcdGltZXMgUChcdGV4dHtBZ2V9ID4gNTB8XHRleHR7SGVhcnQgRGlzZWFzZX0gPSBcdGV4dHtub30pIFx0aW1lcyBQKFx0ZXh0e0Nob2xlc3Rlcm9sfSA9IFx0ZXh0e2hpZ2h9fFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7bm99KSBcdGltZXMgXGxkb3RzCiAgICAkJAoKICAgIFVzaW5nIG91ciBhc3N1bWVkIGxpa2VsaWhvb2RzIGFuZCBwcmlvciBwcm9iYWJpbGl0aWVzLCB3ZSBjYWxjdWxhdGUgdGhlc2UgdmFsdWVzLgoKNC4gICoqQ29tcGFyaXNvbiBhbmQgUHJlZGljdGlvbjoqKgoKICAgIC0gICBDb21wYXJlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllczoKICAgICAgICAtICAgSWYgJFAoXHRleHR7SGVhcnQgRGlzZWFzZX0gPSBcdGV4dHt5ZXN9fFx0ZXh0e2ZlYXR1cmVzfSkgPiBQKFx0ZXh0e0hlYXJ0IERpc2Vhc2V9ID0gXHRleHR7bm99fFx0ZXh0e2ZlYXR1cmVzfSkkLCBjbGFzc2lmeSB0aGUgcGF0aWVudCBhcyBoYXZpbmcgaGVhcnQgZGlzZWFzZS4KICAgICAgICAtICAgT3RoZXJ3aXNlLCBjbGFzc2lmeSB0aGUgcGF0aWVudCBhcyBub3QgaGF2aW5nIGhlYXJ0IGRpc2Vhc2UuCgojIyMjIEltcGxlbWVudGF0aW9uIEV4YW1wbGUgaW4gUgoKSGVyZeKAmXMgYSBzaW1wbGUgUiBpbXBsZW1lbnRhdGlvbiB1c2luZyB0aGUgYGUxMDcxYCBwYWNrYWdlOgoKYGBgIHIKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShlMTA3MSkKCiMgU2FtcGxlIGRhdGEKZGF0YSA8LSBkYXRhLmZyYW1lKAogIGFnZSA9IGFzLmZhY3RvcihjKCJhYm92ZV81MCIsICJiZWxvd181MCIsICJhYm92ZV81MCIsICJiZWxvd181MCIsICJhYm92ZV81MCIpKSwKICBjaG9sZXN0ZXJvbCA9IGFzLmZhY3RvcihjKCJoaWdoIiwgIm5vcm1hbCIsICJoaWdoIiwgIm5vcm1hbCIsICJoaWdoIikpLAogIGJwID0gYXMuZmFjdG9yKGMoImhpZ2giLCAibm9ybWFsIiwgImhpZ2giLCAibm9ybWFsIiwgImhpZ2giKSksCiAgY2hlc3RfcGFpbiA9IGFzLmZhY3RvcihjKCJ5ZXMiLCAibm8iLCAieWVzIiwgIm5vIiwgInllcyIpKSwKICBleGVyY2lzZV9hbmdpbmEgPSBhcy5mYWN0b3IoYygieWVzIiwgIm5vIiwgInllcyIsICJubyIsICJ5ZXMiKSksCiAgaGVhcnRfZGlzZWFzZSA9IGFzLmZhY3RvcihjKCJ5ZXMiLCAibm8iLCAieWVzIiwgIm5vIiwgInllcyIpKQopCgojIFRyYWluIHRoZSBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyCm1vZGVsIDwtIG5haXZlQmF5ZXMoaGVhcnRfZGlzZWFzZSB+IC4sIGRhdGEgPSBkYXRhKQoKIyBQcmVkaWN0IG9uIG5ldyBkYXRhCm5ld19wYXRpZW50IDwtIGRhdGEuZnJhbWUoCiAgYWdlID0gZmFjdG9yKCJhYm92ZV81MCIsIGxldmVscyA9IGxldmVscyhkYXRhJGFnZSkpLAogIGNob2xlc3Rlcm9sID0gZmFjdG9yKCJoaWdoIiwgbGV2ZWxzID0gbGV2ZWxzKGRhdGEkY2hvbGVzdGVyb2wpKSwKICBicCA9IGZhY3RvcigiaGlnaCIsIGxldmVscyA9IGxldmVscyhkYXRhJGJwKSksCiAgY2hlc3RfcGFpbiA9IGZhY3RvcigieWVzIiwgbGV2ZWxzID0gbGV2ZWxzKGRhdGEkY2hlc3RfcGFpbikpLAogIGV4ZXJjaXNlX2FuZ2luYSA9IGZhY3RvcigieWVzIiwgbGV2ZWxzID0gbGV2ZWxzKGRhdGEkZXhlcmNpc2VfYW5naW5hKSkKKQoKcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsLCBuZXdfcGF0aWVudCkKcHJpbnQocHJlZGljdGlvbikKYGBgCgojIyBOdW1lcmljIEZlYXR1cmVzCgpUaGUgTmFpdmUgQmF5ZXMgYWxnb3JpdGhtIHJlcXVpcmVzIGNvdW50aW5nIG9mIGZyZXF1ZW5jaWVzIHNvIGNhdGVnb3JpY2FsIGZlYXR1cmVzIGFyZSBuZWNlc3NhcnkuIFdoZW4gZGVhbGluZyB3aXRoIG51bWVyaWMgcmF0aGVyIHRoYW4gY2F0ZWdvcmljYWwgZmVhdHVyZXMgaW4gYSBkYXRhc2V0IGZvciBOYWl2ZSBCYXllcyBjbGFzc2lmaWNhdGlvbiwgZGF0YSBzY2llbnRpc3RzIGhhdmUgc2V2ZXJhbCBzdHJhdGVnaWVzIHRvIGFkYXB0IHRoZSBhbGdvcml0aG0sIHdoaWNoIHRyYWRpdGlvbmFsbHkgYXNzdW1lcyBjYXRlZ29yaWNhbCBmZWF0dXJlcy4gSGVyZSBhcmUgc29tZSBjb21tb24gYXBwcm9hY2hlczoKCiMjIyBHYXVzc2lhbiBOYWl2ZSBCYXllcwoKRm9yIG51bWVyaWMgZmVhdHVyZXMsIHRoZSBHYXVzc2lhbiBOYWl2ZSBCYXllcyAoR05CKSBpcyBhIGNvbW1vbiB2YXJpYXRpb24uIEl0IGFzc3VtZXMgdGhhdCB0aGUgbnVtZXJpYyBmZWF0dXJlcyBmb2xsb3cgYSBHYXVzc2lhbiAobm9ybWFsKSBkaXN0cmlidXRpb24uIFRoaXMgdmVyc2lvbiBvZiBOYWl2ZSBCYXllcyB1c2VzIHRoZSBmb2xsb3dpbmcgZm9ybXVsYSB0byBjb21wdXRlIHRoZSBsaWtlbGlob29kIG9mIHRoZSBkYXRhOgoKJCQgUCh4X2l8QykgPSBcZnJhY3sxfXtcc3FydHsyXHBpXHNpZ21hX0NeMn19IFxleHBcbGVmdCggLVxmcmFjeyh4X2kgLSBcbXVfQyleMn17MlxzaWdtYV9DXjJ9IFxyaWdodCkgJCQKCndoZXJlICRcbXVfQyQgYW5kICRcc2lnbWFfQyQgYXJlIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGZlYXR1cmUgJHhfaSQgZm9yIGNsYXNzICRDJCwgcmVzcGVjdGl2ZWx5LiBEdXJpbmcgdGhlIHRyYWluaW5nIHBoYXNlLCB0aGUgcGFyYW1ldGVycyAkXG11X0MkIGFuZCAkXHNpZ21hX0MkIGFyZSBlc3RpbWF0ZWQgZm9yIGVhY2ggZmVhdHVyZSBhbmQgZWFjaCBjbGFzcy4KCiMjIyBEaXNjcmV0aXphdGlvbiAoQmlubmluZykKCkFub3RoZXIgYXBwcm9hY2ggaXMgdG8gY29udmVydCB0aGUgbnVtZXJpYyBmZWF0dXJlcyBpbnRvIGNhdGVnb3JpY2FsIGZlYXR1cmVzIHRocm91Z2ggYSBwcm9jZXNzIGNhbGxlZCBkaXNjcmV0aXphdGlvbiBvciBiaW5uaW5nLiBUaGlzIGludm9sdmVzIGRpdmlkaW5nIHRoZSByYW5nZSBvZiBudW1lcmljIHZhbHVlcyBpbnRvIGRpc2NyZXRlIGludGVydmFscyBhbmQgdHJlYXRpbmcgZWFjaCBpbnRlcnZhbCBhcyBhIGNhdGVnb3J5LiBGb3IgZXhhbXBsZSwgYWdlIG1pZ2h0IGJlIGNvbnZlcnRlZCBpbnRvIGJpbnMgc3VjaCBhcyAiMC0xOCIsICIxOS0zNSIsICIzNi01MCIsICI1MSsiLgoKYGBgIHIKIyBEaXNjcmV0aXplIHRoZSBudW1lcmljIGZlYXR1cmUKZGF0YSRhZ2VfYmluIDwtIGN1dChkYXRhJGFnZSwgYnJlYWtzID0gYygtSW5mLCAxOCwgMzUsIDUwLCBJbmYpLCBsYWJlbHMgPSBjKCIwLTE4IiwgIjE5LTM1IiwgIjM2LTUwIiwgIjUxKyIpKQoKIyBUcmFpbiBOYWl2ZSBCYXllcyBtb2RlbCB1c2luZyB0aGUgYmlubmVkIGZlYXR1cmUKbW9kZWwgPC0gbmFpdmVCYXllcyhoZWFydF9kaXNlYXNlIH4gYWdlX2JpbiArIGNob2xlc3Rlcm9sICsgYnAgKyBjaGVzdF9wYWluICsgZXhlcmNpc2VfYW5naW5hLCBkYXRhID0gZGF0YSkKYGBgCgojIyMgS2VybmVsIERlbnNpdHkgRXN0aW1hdGlvbgoKRm9yIG51bWVyaWMgZmVhdHVyZXMgdGhhdCBkbyBub3QgZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiAoS0RFKSBjYW4gYmUgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiBvZiB0aGUgZmVhdHVyZS4gVGhpcyBub24tcGFyYW1ldHJpYyBhcHByb2FjaCBpcyBtb3JlIGZsZXhpYmxlIHRoYW4gYXNzdW1pbmcgYSBzcGVjaWZpYyBkaXN0cmlidXRpb24gbGlrZSBHYXVzc2lhbi4KCiMjIyBIYW5kbGluZyBNaXhlZCBGZWF0dXJlcwoKSW4gbWFueSByZWFsLXdvcmxkIGRhdGFzZXRzLCB5b3UnbGwgaGF2ZSBib3RoIGNhdGVnb3JpY2FsIGFuZCBudW1lcmljIGZlYXR1cmVzLiBJbiBzdWNoIGNhc2VzLCBpdCdzIGNvbW1vbiB0byB1c2UgYSBtaXhlZCBhcHByb2FjaCB3aGVyZSBjYXRlZ29yaWNhbCBmZWF0dXJlcyBhcmUgaGFuZGxlZCB3aXRoIHRyYWRpdGlvbmFsIE5haXZlIEJheWVzIG1ldGhvZHMsIGFuZCBudW1lcmljIGZlYXR1cmVzIGFyZSBoYW5kbGVkIHVzaW5nIEdhdXNzaWFuIE5haXZlIEJheWVzIG9yIGFub3RoZXIgc3VpdGFibGUgbWV0aG9kLgoKIyMjIEV4YW1wbGUgaW4gUgoKSGVyZeKAmXMgaG93IHRvIGltcGxlbWVudCBHYXVzc2lhbiBOYWl2ZSBCYXllcyBpbiBSIHVzaW5nIHRoZSBgZTEwNzFgIHBhY2thZ2Ugd2hpY2ggYXV0b21hdGljYWxseSBhZGp1c3RzIHRoZSBhbGdvcml0aG06CgpgYGB7ciBnYXVzc2lhbkJheWVzLCBlY2hvPVQsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPVR9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoZTEwNzEpCgojIFNhbXBsZSBkYXRhCmRhdGEgPC0gZGF0YS5mcmFtZSgKICBhZ2UgPSBjKDI1LCA0NSwgMzUsIDUwLCAyMywgMzQpLAogIGNob2xlc3Rlcm9sID0gYygyMDAsIDI1MCwgMTgwLCAyMTAsIDE5MCwgMjQwKSwKICBicCA9IGMoMTMwLCAxNDAsIDEyMCwgMTUwLCAxMTAsIDEzNSksCiAgY2hlc3RfcGFpbiA9IGFzLmZhY3RvcihjKCJ5ZXMiLCAibm8iLCAieWVzIiwgIm5vIiwgInllcyIsICJubyIpKSwKICBleGVyY2lzZV9hbmdpbmEgPSBhcy5mYWN0b3IoYygibm8iLCAieWVzIiwgIm5vIiwgInllcyIsICJubyIsICJ5ZXMiKSksCiAgaGVhcnRfZGlzZWFzZSA9IGFzLmZhY3RvcihjKCJ5ZXMiLCAibm8iLCAieWVzIiwgIm5vIiwgInllcyIsICJubyIpKQopCgojIFRyYWluIHRoZSBHYXVzc2lhbiBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyCm1vZGVsIDwtIG5haXZlQmF5ZXMoaGVhcnRfZGlzZWFzZSB+IGFnZSArIGNob2xlc3Rlcm9sICsgYnAgKyBjaGVzdF9wYWluICsgZXhlcmNpc2VfYW5naW5hLCBkYXRhID0gZGF0YSkKCiMgUHJlZGljdCBvbiBuZXcgZGF0YQpuZXdfcGF0aWVudCA8LSBkYXRhLmZyYW1lKAogIGFnZSA9IDQ1LAogIGNob2xlc3Rlcm9sID0gMjIwLAogIGJwID0gMTQwLAogIGNoZXN0X3BhaW4gPSBmYWN0b3IoInllcyIsIGxldmVscyA9IGxldmVscyhkYXRhJGNoZXN0X3BhaW4pKSwKICBleGVyY2lzZV9hbmdpbmEgPSBmYWN0b3IoIm5vIiwgbGV2ZWxzID0gbGV2ZWxzKGRhdGEkZXhlcmNpc2VfYW5naW5hKSkKKQoKcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsLCBuZXdfcGF0aWVudCkKcHJpbnQocHJlZGljdGlvbikKYGBgCgojIyMgQ29uY2x1c2lvbgoKQWRhcHRpbmcgTmFpdmUgQmF5ZXMgZm9yIG51bWVyaWMgZmVhdHVyZXMgaW52b2x2ZXMgdXNpbmcgdGVjaG5pcXVlcyBsaWtlIEdhdXNzaWFuIE5haXZlIEJheWVzLCBkaXNjcmV0aXphdGlvbiwgb3Iga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbi4gRWFjaCBhcHByb2FjaCBoYXMgaXRzIHN0cmVuZ3RocyBhbmQgaXMgc3VpdGFibGUgZm9yIGRpZmZlcmVudCB0eXBlcyBvZiBkYXRhIGFuZCBhcHBsaWNhdGlvbnMuIEJ5IHNlbGVjdGluZyB0aGUgYXBwcm9wcmlhdGUgbWV0aG9kLCBkYXRhIHNjaWVudGlzdHMgY2FuIGVmZmVjdGl2ZWx5IGFwcGx5IE5haXZlIEJheWVzIHRvIGRhdGFzZXRzIHdpdGggbnVtZXJpYyBmZWF0dXJlcy4KCiMjIyBTdW1tYXJ5CgpUaGUgTmFpdmUgQmF5ZXMgY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtLCBkZXNwaXRlIGl0cyBzaW1wbGljaXR5IGFuZCB0aGUgbmFpdmUgYXNzdW1wdGlvbiBvZiBmZWF0dXJlIGluZGVwZW5kZW5jZSwgcmVtYWlucyBhIHBvcHVsYXIgYW5kIGVmZmVjdGl2ZSBtZXRob2QgZm9yIG1hbnkgY2xhc3NpZmljYXRpb24gdGFza3MuIEl0cyBmb3VuZGF0aW9uIGluIEJheWVzJyBUaGVvcmVtIGFuZCB0aGUgcmVzdWx0aW5nIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeSBtYWtlIGl0IGVzcGVjaWFsbHkgc3VpdGFibGUgZm9yIGxhcmdlLXNjYWxlIHByb2JsZW1zLiBVbmRlcnN0YW5kaW5nIGl0cyB0aGVvcmV0aWNhbCB1bmRlcnBpbm5pbmdzIGFuZCBwcmFjdGljYWwgYXBwbGljYXRpb25zIGlzIGVzc2VudGlhbCBmb3IgYW55IG1hY2hpbmUgbGVhcm5pbmcgcHJhY3RpdGlvbmVyLgoKVGhlIGNvbmRpdGlvbmFsIGluZGVwZW5kZW5jZSBhc3N1bXB0aW9uIGluIHRoZSBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyIGlzIGVzc2VudGlhbCBmb3IgbWFraW5nIHRoZSBtb2RlbCBjb21wdXRhdGlvbmFsbHkgZWZmaWNpZW50LCBzY2FsYWJsZSwgYW5kIGZlYXNpYmxlIHRvIHRyYWluIHdpdGggcmVhbGlzdGljIGFtb3VudHMgb2YgZGF0YS4gV2hpbGUgdGhpcyBhc3N1bXB0aW9uIGlzIG9mdGVuIG5vdCBzdHJpY3RseSB0cnVlLCB0aGUgcmVzdWx0aW5nIG1vZGVsIG9mdGVuIHBlcmZvcm1zIHdlbGwgaW4gcHJhY3RpY2UgZHVlIHRvIHRoZSBvdmVyYWxsIHJvYnVzdG5lc3Mgb2YgdGhlIGFwcHJvYWNoIGFuZCB0aGUgdGVuZGVuY3kgb2YgZXJyb3JzIHRvIGNhbmNlbCBvdXQgYWNyb3NzIG1hbnkgZmVhdHVyZXMuIFRoaXMgbWFrZXMgdGhlIE5haXZlIEJheWVzIGNsYXNzaWZpZXIgYSB2YWx1YWJsZSB0b29sIGluIHRoZSBtYWNoaW5lIGxlYXJuaW5nIHRvb2xib3gsIGVzcGVjaWFsbHkgZm9yIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSBhbmQgYXBwbGljYXRpb25zIHJlcXVpcmluZyBxdWljayBhbmQgZWZmaWNpZW50IGNsYXNzaWZpY2F0aW9uLgoKVGhlIE5haXZlIEJheWVzIENsYXNzaWZpZXIgYWxnb3JpdGhtIGlzIHF1aXRlIHZlcnNhdGlsZSBhbmQgY2FuIGJlIGFwcGxpZWQgdG8gYSB3aWRlIHJhbmdlIG9mIGFwcGxpY2F0aW9ucywgcGFydGljdWxhcmx5IGluIGRvbWFpbnMgd2hlcmUgZmVhdHVyZSBpbmRlcGVuZGVuY2UgY2FuIGJlIHJlYXNvbmFibHkgYXNzdW1lZCBvciB0aGUgYmVuZWZpdHMgb2YgdGhlIGFsZ29yaXRobSdzIGVmZmljaWVuY3kgb3V0d2VpZ2ggdGhlIHBvdGVudGlhbCBpbmFjY3VyYWNpZXMgaW50cm9kdWNlZCBieSB0aGUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24uIFRoaXMgbWFrZXMgTmFpdmUgQmF5ZXMgYSB2YWx1YWJsZSB0b29sIGluIHRoZSBtYWNoaW5lIGxlYXJuaW5nIHByYWN0aXRpb25lcuKAmXMgYXJzZW5hbCwgZXNwZWNpYWxseSBpbiB0aGUgYXJlYXMgb2YgdGV4dCBjbGFzc2lmaWNhdGlvbiwgYW5vbWFseSBkZXRlY3Rpb24sIGFuZCBtZWRpY2FsIGRpYWdub3N0aWNzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKTm8gcmVmZXJlbmNlcy4KCiMjIEVycmF0YQoKTm9uZSBjb2xsZWN0ZWQgeWV0LiBbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==