1. PREPARE
Each machine learning “case study” is designed to illustrate how
machine learning methods and techniques can be applied to address a
research question of interest, create useful data products, and conduct
reproducible research. Each case study is structured around a basic
analytics workflow modeled after the Data-Intensive Research Workflow
from Learning Analytics Goes to School (Krumm et
al., 2018):

Figure 2.2 Steps of Data-Intensive Research Workflow
In the overview presentation for this lab, we considered five steps
in our supervised machine learning process. Those steps are mirrored
here in this case study, with the addition of some other components of
this workflow. For example, to help prepare for analysis, we’ll first
take a step back and think about how we want to use machine learning,
and predicting is a key word. Many scholars have focused on
predicting students who are at-risk: of dropping a course or
not succeeding in it. In the ML Lab 1 case study will cover the
following workflow topics as we attempt to develop our own model for
predicting student drop-out:
Prepare: Prior to analysis, we’ll look at the
context from which our data came, formulate a basic research question,
and get introduced the {tidymodels} packages for machine
learning.
Wrangle: Wrangling data entails the work of
cleaning, transforming, and merging data. In Part 2 we focus on
importing CSV files and modifying some of our variables.
Explore: We take a quick look at our variables
of interest and do some basic “feature engineering” by creating some new
variables we think will be predictive of students at risk.
Model: We dive deeper into the five steps in our
supervised machine learning process, focusing on the mechanics of
making predictions.
Communicate: To wrap up our case study, we’ll
create our first “data product” and share our analyses and findings by
creating our first web page using R Markdown.
1a. Conceptual Focus
Conceptually, we focus on prediction and how it differs from the
goals of description or explanation. We have two readings in Lab 1 that
accompany this. The first reading introduced below focuses on this
distinction between prediction and description or explanation. It is one
of the most widely-read papers in machine learning and articulates how
machine learning differs from other kinds of statistical models. Breiman
describes the difference in terms of data modeling (models for
description and explanation) and algorithmic modeling (what we
call prediction or machine learning models)*
Research Question
Technically, we’ll focus on the core parts of doing a machine
learning analysis in R. We’ll use the {tidymodels} set of R packages
(add-ons) to do so. However, to help anchor our analysis and provide us
with some direction, we’ll focus on the following research question as
we explore this new:
How well can we predict students who are at risk of dropping a
course?
Reading: Statistical modeling: The two cultures
Breiman, L. (2001). Statistical modeling: The two cultures (with
comments and a rejoinder by the author). Statistical Science,
16(3), 199-231. https://projecteuclid.org/journals/statistical-science/volume-16/issue-3/Statistical-Modeling--The-Two-Cultures-with-comments-and-a/10.1214/ss/1009213726.pdf
👉 Your Turn ⤵
You’ll be asked to reflect more deeply on this article later on (in
the badge activity); but for now, open up the article and take quick
scan of the article and note below an observation or question you have
about the article.
An observation that I have identified as concerning in this
publication is the author’s position on published results. They conclude
that it is often the case that professionals put less emphasis on
developing a model that fits the problem they are attempting to solve
than they do on a model that is in appearance, expertly crafted
(Breiman, 203). In essence, complexity is favored over usability.
Reading: Predicting students’ final grades
Estrellado, R. A., Freer, E. A., Mostipak, J., Rosenberg, J. M.,
& Velásquez, I. C. (2020). Data science in education using
R. Routledge (c14), Predicting students’ final grades using machine
learning methods with online course data. http://www.datascienceineducation.com/
Please review this chapter, focusing on the overall goals of the
analysis and how the analysis was presented (focusing on predictions,
rather than the ways we may typically interpret a statistical model–like
measures of statistical significance).
1b. Load Packages
As highlighted in Chapter 6 of Data
Science in Education Using R (DSIEUR), one of the first steps of
every workflow should be to set up your “Project” within RStudio. Recall
that:
A Project is the home for all of the files, images,
reports, and code that are used in any given project
Since we are working from an R project cloned from GitHub, a Project has already
been set up for you as indicated by the .Rproj
file in your
main directory in the Files pane. Instead, we will focus on getting our
project set up with the requisite packages we’ll need for analysis.
Packages, sometimes called libraries, are shareable
collections of R code that can contain functions, data, and/or
documentation and extend the functionality of R. You can always check to
see which packages have already been installed and loaded into RStudio
or RStudio Cloud by looking at the Files, Plots, & Packages Pane in
the lower right-hand corner.
Two packages we’ll use extensively throughout these labs are the
{tidyverse} and {tidymodels} packages.
tidyverse 📦

One package that we’ll be using extensively throughout LASER is the
{tidyverse} package. Recall from earlier tutorials that the {tidyverse}
package is actually a collection of R packages
designed for reading, wrangling, and exploring data and which all share
an underlying design philosophy, grammar, and data structures. These
shared features are sometimes “tidy data principles.”
Click the green arrow in the right corner of the “code chunk” that
follows to load the {tidyverse} library.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.0 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
tidymodels

The tidymodels package is a
“meta-package” for modeling and statistical analysis that shares the
underlying design philosophy, grammar, and data structures of the tidyverse. It includes a core set
of packages that are loaded on startup and contains tools for:
data splitting and pre-processing;
model selection, tuning, and evaluation;
feature selection and variable importance estimation;
as well as other functionality.
👉 Your Turn ⤵
In addition to the {tidymodels} package, we’ll also be using the
lightweight but highly useful {janitor} package to help with some data
cleaning tasks. Use the code chunk below to load these two packages:
As a tip, remember to use the library()
function to load
these packages. After you’ve done that, click the green arrow to run the
code chunk. If you see a bunch of messages (not anything labeled as an
error), you are good to go! These messages mean the packages loaded
correctly.
2. WRANGLE
In general, data wrangling involves some combination of cleaning,
reshaping, transforming, and merging data (Wickham & Grolemund,
2017). The importance of data wrangling is difficult to overstate, as it
involves the initial steps of going from raw data to a dataset that can
be explored and modeled (Krumm et al, 2018). In Part 2, we focus on the
the following wrangling processes to:
Importing and Inspecting Data. In this section,
we will “read” in our CSV data file and take a quick look at what our
file contains.
Mutate Variables. We use the
mutate()
function to create a dichotomous variable for
whether or not the student withdrew from the course.
1a. Import and Inspect Data
For learning labs 1-3, we’ll be using a widely-used data set in the
learning analytics field: the Open University
Learning Analytics Dataset (OULAD). The OULAD was created by
learning analytics researchers at the United Kingdom-based Open
University. It includes data from post-secondary learners participation
in one of several Massive Open Online Courses (called modules
in the OULAD).
Kuzilek, J., Hlosta, M., & Zdrahal, Z. (2017). Open university
learning analytics dataset. Scientific Data, 4(1), 1-8. https://www.nature.com/articles/sdata2017171
Abstract
Learning Analytics focuses on the collection and analysis of
learners’ data to improve their learning experience by providing
informed guidance and to optimise learning materials. To support the
research in this area we have developed a dataset, containing data from
courses presented at the Open University (OU). What makes the dataset
unique is the fact that it contains demographic data together with
aggregated clickstream data of students’ interactions in the Virtual
Learning Environment (VLE). This enables the analysis of student
behavior, represented by their actions. The dataset contains the
information about 22 courses, 32,593 students, their assessment results,
and logs of their interactions with the VLE represented by daily
summaries of student clicks (10,655,280 entries). The dataset is freely
available at https://analyse.kmi.open.ac.uk/open_dataset under a
CC-BY 4.0 license.
👉 Your Turn ⤵
You don’t need to read the entire article yet, but please open this
article, scan the sections, and write down two things you notice or
wonder about the dataset.
One of the first thoughts I had had, being in higher education
and looking to research with student data is the natural concern of
FERPA violations with respect to personally identifiable information.
OULAD appears to have a robust system of not only informing students of
the use of data, but effectively scrubbing the data for any information
which could be harmful to the student if released.
Of the three different data types used in the OULAD Research
(Demographic, Performance, and Learning Behavior), I am most interested
to see what is included in learning behavior in order to translate that
information for my own use.
Read CSV Data File
The data can be downloaded at the above link; however, for our
purposes, they are already downloaded to the data
sub-folder.
We’ll use the read_csv()
function to load the files –
two in total, with data on students and assessments. Note: we have done
some minimal processing of these files to make getting us started
easier. If you’re interested in what we’ve done, check out the
oulad.R
file in the lab-1
folder.
For now, please read in the oulad-students.csv
file. Use
the read_csv()
function to do this, paying attention to
where those files are located relative to this case study file.
students <- read_csv("oulad-students.csv")
## Rows: 32593 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): code_module, code_presentation, gender, region, highest_education, ...
## dbl (6): id_student, num_of_prev_attempts, studied_credits, module_presentat...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
You can see a description of the data here.
The students file includes three files joined together: studentInfo,
courses, and studentRegistration. Take a look at the data description to
get a sense for what variables are in which data frame.
👉 Your Turn ⤵
Inspect Data
In the chunk below, examine the data set using a function or means of
your choice (such as just printing the data set by typing its
name or using the glimpse()
function). Do this in the code
chunk below! Note its dimensions — especially how many rows it has!
glimpse("oulad-students.csv")
## chr "oulad-students.csv"
Write down a few observations after inspecting the data:
2b. “Mutate” Variables
We’re going to do a few more steps related to data wrangling here,
noting we could also do these at later stages of our process (namely, in
the feature engineering stage).
First, since we are interested in developing a model that can predict
whether a student is at risk of dropping a course, and so we can
intervene before that happens, we need an outcome variable that let’s us
know if they have passed.
To create this variable, let’s use the mutate()
function
to create a dichotomous variable for whether or not the student withdrew
from the course. Here’s a way we can do this, using
if_else()
and as.factor()
. This will be our
outcome variable, or the predicted variable.
students <- students %>%
mutate(pass = ifelse(final_result == "Pass", 1, 0)) %>% # creates a new variable named "pass" and a dummy code of 1 if value of final_result equals "pass" and 0 if not
mutate(pass = as.factor(pass)) # makes the variable a factor, helping later steps
Note: The mutate()
function.) is a critical function to learn and is used to create new
columns that are functions of existing variables. It can also modify (if
the name is the same as an existing column) and delete columns (by
setting their value to NULL
).
Next, let’s do something similar for whether a student identifies as
having a disability. In this case, there are only two values for the
disability variable, so we can simply convert it directly to a factor.
Look at the code you used above, modifying it for the
disability variable. This will be an independent variable, or a
predictor variable.
students <- students %>%
mutate(disability = as.factor(disability))
👉 Your Turn ⤵
In the chunk below, use the view()
function to manually
check and see if our new variable has indeed been added to our data
frame.
view("disability")
Write down a few observations after inspecting the data:
For me this opens up a 2X2 table with disability as an ‘x’ variable
value. I honestly am not sure if I am doing this correctly.
Note: The mutate()
function.) is a critical function to learn and is used to create new
columns that are functions of existing variables. It can also modify (if
the name is the same as an existing column) and delete columns (by
setting their value to NULL
).
Next, let’s do something similar for whether a student identifies as
having a disability. In this case, there are only two values for the
disability variable, so we can simply convert it directly to a factor.
Look at the code you used above, modifying it for the
disability variable. This will be an independent variable, or a
predictor variable.
students <- students %>%
mutate(disability = as.factor(disability))
3. EXPLORE
As noted by Krumm et al. (2018), exploratory data analysis often
involves some combination of data visualization and feature
engineering. In Part 3, we will create a quick visualization to
help us spot any potential issues with our data and engineer new
predictive variables or “features” that we will use in our predictive
models. Specifically, in Part 3 we will:
Examine Outcomes by taking a quick
count()
of the number of students and the number of
specific offerings of each course module.
Engineer Predictors by creating one more
predictor variable based on a measure of socioeconomic resources–the
index of multiple depravity variable.
3a. Examine Variables
A useful function for exploring data is count()
; it does
what it sounds like! It counts how many times values for a variable
appear.
Referring to the data
description, in the chunk below, count the number of students. Also,
count the number of courses (modules) and specific offerings (as modules
can be offered multiple times per year). Learn more about
count()
here.
students %>%
count(id_student) # this many students
## # A tibble: 28,785 × 2
## id_student n
## <dbl> <int>
## 1 3733 1
## 2 6516 1
## 3 8462 2
## 4 11391 1
## 5 23629 1
## 6 23632 1
## 7 23698 1
## 8 23798 1
## 9 24186 1
## 10 24213 2
## # ℹ 28,775 more rows
students %>%
count(code_module, code_presentation) # this many offerings
## # A tibble: 22 × 3
## code_module code_presentation n
## <chr> <chr> <int>
## 1 AAA 2013J 383
## 2 AAA 2014J 365
## 3 BBB 2013B 1767
## 4 BBB 2013J 2237
## 5 BBB 2014B 1613
## 6 BBB 2014J 2292
## 7 CCC 2014B 1936
## 8 CCC 2014J 2498
## 9 DDD 2013B 1303
## 10 DDD 2013J 1938
## # ℹ 12 more rows
3b. Feature Engineering
As defined by Krumm, Means, and Bienkowski (2018) in Learning
Analytics Goes to School:
Feature engineering is the process of creating new
variables within a dataset, which goes above and beyond the work of
recoding and rescaling variables.
The authors note that feature engineering draws on substantive
knowledge from theory or practice, experience with a particular data
system, and general experience in data-intensive research. Moreover,
these features can be used not only in machine learning models, but also
in visualizations and tables comprising descriptive statistics.
Though not often discussed, feature engineering is an important
element of data-intensive research that can generate new insights and
improve predictive models. You can read more about feature engineering
here.
Student Socioeconomic Index
For our first lab, we’ll engage in a very basic feature engineering
step, though we’ll do this much more in the next learning
lab.
To do feature engineering, let’s create one more predictor variable
based on a measure of socioeconomic resources–the index of multiple
depravity variable. The process we take here is to turn this variable
that is a character string into a number by creating a factor and then
coercing it to an integer.
👉 Your Turn ⤵
Please replace the ____ values in the code below with the correct
variable.
students <- students %>%
mutate(imd_band = factor(imd_band, levels = c("0-10%",
"10-20%",
"20-30%",
"30-40%",
"40-50%",
"50-60%",
"60-70%",
"70-80%",
"80-90%",
"90-100%"))) %>% # this creates a factor with ordered levels
mutate(imd_band = as.integer(imd_band)) # this changes the levels into integers based on the order of the factor levels
students
## # A tibble: 32,593 × 16
## code_module code_presentation id_student gender region highest_education
## <chr> <chr> <dbl> <chr> <chr> <chr>
## 1 AAA 2013J 11391 M East Angli… HE Qualification
## 2 AAA 2013J 28400 F Scotland HE Qualification
## 3 AAA 2013J 30268 F North West… A Level or Equiv…
## 4 AAA 2013J 31604 F South East… A Level or Equiv…
## 5 AAA 2013J 32885 F West Midla… Lower Than A Lev…
## 6 AAA 2013J 38053 M Wales A Level or Equiv…
## 7 AAA 2013J 45462 M Scotland HE Qualification
## 8 AAA 2013J 45642 F North West… A Level or Equiv…
## 9 AAA 2013J 52130 F East Angli… A Level or Equiv…
## 10 AAA 2013J 53025 M North Regi… Post Graduate Qu…
## # ℹ 32,583 more rows
## # ℹ 10 more variables: imd_band <int>, age_band <chr>,
## # num_of_prev_attempts <dbl>, studied_credits <dbl>, disability <fct>,
## # final_result <chr>, module_presentation_length <dbl>,
## # date_registration <dbl>, date_unregistration <dbl>, pass <fct>
We’re now ready to proceed to the five machine learning steps!
4. MODEL
Recall from our first reading that there are two general types of
modeling approaches: unsupervised and supervised machine learning. In
Part 4, we focus on supervised learning models, which are used to
quantify relationships between features (e.g., motivation and
performance) and a known outcome (e.g., student drop out). These models
can be used for classification of binary or categorical outcomes, as
we’ll illustrate in this section, or regression as we’ll demonstrate in
Learning Lab 2.
Specifically, in Part 4 we will learn how to:
Split Data into a training and test set that
will be used to develop a predictive model;
Create a “Recipe” for our predictive model and
learn how to deal with nominal data that we would like to use as
predictors;
Specify the model and workflow by selecting the
functional form of the model that we want and using a model
workflow to pair our model and recipe together;
Fit Models to our training set using logistic
regression;
Interpret Accuracy of our model to see how well
our model can “predict” our outcome of interest.
Step 1. Split data
The authors of Data Science in Education Using R (Estrellado et
al.,2020) remind us that:
At its core, machine learning is the process of “showing” your
statistical model only some of the data at once and training the model
to predict accurately on that training dataset (this is the “learning”
part of machine learning). Then, the model as developed on the training
data is shown new data - data you had all along, but hid from your
computer initially - and you see how well the model that you developed
on the training data performs on this new testing data. Eventually, you
might use the model on entirely new data.
Training and Testing Sets
It is therefore common when beginning a modeling project to separate the
data set into two partitions:
The training set is used to estimate, develop and
compare models; feature engineering techniques; tune models,
etc.
The test set is held in reserve until the end of the
project, at which point there should only be one or two models under
serious consideration. It is used as an unbiased source for measuring
final model performance.
There are different ways to create these partitions of the data and
there is no uniform guideline for determining how much data should be
set aside for testing. The proportion of data can be driven by many
factors, including the size of the original pool of samples and the
total number of predictors.
After you decide how much to set aside, the most common approach for
actually partitioning your data is to use a random sample. For our
purposes, we’ll use random sampling to select 20% for the test set and
use the remainder for the training set, which are the defaults for the
{rsample}
package.
Split Data Sets
To split our data, we will be using our first {tidymodels} function -
initial_split()
.
The function initial_split()
function from the {rsample}
package takes the original data and saves the information on how to make
the partitions. The {rsample} package has two aptly named functions for
created a training and testing data set called training()
and testing()
, respectively.
Run the following code to split the data:
Note: Since random sampling uses random numbers, it
is important to set the random number seed using the
set.seed()
function. This ensures that the random numbers
can be reproduced at a later time (if needed). We pick the first date on
which we may carry out this learning lab as the seed, but any number
will work!
👉 Your Turn ⤵
Go ahead and type data_train
and data_test
into the console (in steps) to check that this data set indeed has 80%
of the number of observations as in the larger data. Do that in the
chunk below:
Step 2: Create a “Recipe”
In this section, we introduce another tidymodels package named {recipes}, which is designed
to help you prepare your data before training your model.
Recipes are built as a series of preprocessing steps, such as:
converting qualitative predictors to indicator variables (also
known as dummy variables),
transforming data to be on a different scale (e.g., taking the
logarithm of a variable),
transforming whole groups of predictors together,
extracting key features from raw variables (e.g., getting the day
of the week out of a date variable), and so on.
If you are familiar with R’s formula interface, a lot of this might
sound familiar and like what a formula already does. Recipes can be used
to do many of the same things, but they have a much wider range of
possibilities.
Step 3: Specify the model and workflow
With tidymodels, we start building a model by specifying the
functional form of the model that we want using the {parsnip} package.
Since our outcome is binary, the model type we will use is “logistic
regression.” We can declare this with logistic_reg()
and assign to an object we will later use in our workflow:
Run the following code to finish specifying our model:
That is pretty underwhelming since, on its own, it doesn’t really do
much. However, now that the type of model has been specified, a method
for fitting or training the model can be stated using the
engine.
Start your engine
To set the engine, let’s rewrite the code above and “pipe”
in the set_engine("glm")
function and
set_mode("classification"))
to set the “mode” to
classification. Note that this could also be changed to regression for a
continuous/numeric outcome.
Run the following code to finish specifying our model:
The engine value is often a mash-up of different packages that can be
used to fit or train the model as well as the estimation method. For
example, we will use “glm” a generalized linear model for binary
outcomes and default for logistic regression in the {parsnip}
package.
Add to workflow
Now we can use the recipe created earlier across several steps as we
train and test our model. To simplify this process, we can use a
model workflow, which pairs a model and recipe together.
This is a straightforward approach because different recipes are
often needed for different models, so when a model and recipe are
bundled, it becomes easier to train and test workflows.
We’ll use the{workflows} package from
tidymodels to bundle our parsnip model (lr_mod
) with our
first recipe (lr_recipe_1
).
Step 4: Fit model
Now that we have a single workflow that can be used to prepare the
recipe and train the model from the resulting predictors, we can use the
fit()
function to fit our model to our
train_data
. And again, we set a random number seed to
ensure that if we run this same code again, we will get the same results
in terms of the data partition:
Finally, we’ll fit our model.
👉 Your Turn ⤵
Importantly, here, we can look at the model. Type
fitted_model
in a code chunk to take a look.
Note that while we don’t typically interpret the coefficients for a
machine learning model, it’s important to recognize that many models
do produce coefficients we could interpret. Instead,
we focus on how the model does with respect to predicting the dependent
variable.
Observations
Write down a brief observation of the output above, focusing on the
coefficients:
- disability has a negative relationship with pass and imd_band has a
positive relationship with pass.
The last_fit function
Finally, we’ll use the last_fit
function, which is the
key here: note that it uses the train_test_split
data—not
just the training data.
Here, then, we fit the data using the training data set and
evaluate its accuracy using the testing data set (which is not
used to train the model).
Type final_fit
below; this is the final, fitted
model—one that can be interpreted further in the next step!
You may see a message/warning above or when you examine
final_fit
; you can safely ignore that.
Step 5: Interpret accuracy
Run the code below to examine the predictions for the test
split of data. Note that the row ID is in the output below, but this
doesn’t correspond one-one to the ID variables used in the
presentation/Shiny.
This is our first set of real output! Note two things:
.pres_class
: This is the predicted code
pass
: This is the known code
When these are the same, the model predicted the
code correctly; when they aren’t the same, the model was
incorrect.
Importantly, we can summarize across all of these codes. One
way to do this is straightforward; how many of the codes were the same,
as in the following chunk of code:
You may notice some of the rows may be missing values. This is
because there were some missing values in the imd_band
variable, and for this machine learning algorithm (the generalized
linear model), missing values result in row-wise deletion.
That’s helpful, but there’s one more step we can take – counting up
the values of correct
:
👉 Your Turn ⤵
Let’s interpret the above. If the value of correct
is
TRUE
when the predicted and known code are the same, what
does the percent
column tell us? Add one or more notes to
the dashes below:
- I cannot seem to get any functions to work in relation to this or
many of the previous concepts. I would guess that ‘percent’ is in
reference to the amount of true values.
A short-cut to the above is below; we’ll use this short-cut
from here forward, having seen how accuracy is calculated.
How accurate was our predictive model? Consider how well our model
would have done by chance alone – what would the accuracy be in that
case (with the model predicting pass one-half of the time)?
Curiously, randomly picking a 0 (did not pass) or a 1 (passed) will
always lead to around a 50% accuracy, regardless of how many
observations are actually associated with a 0 or a 1.
Observation
Let’s step back a bit. How well could we do if we include
more data? And how useful could such a model be in the real
world? We’ll dive into these questions more over the forthcoming
learning labs.
That’s it for now; the core parts of machine learning are used in the
above steps you took; what we’ll do after this leaning lab only adds
nuance and complexity to what we’ve already done.
5. COMMUNICATE
The final step in the workflow/process is sharing the results of your
analysis with wider audience. Krumm et al. (2018) have outlined the
following 3-step process for communicating with education stakeholders
findings from an analysis:
Select. Communicating what one has learned
involves selecting among those analyses that are most important and most
useful to an intended audience, as well as selecting a form for
displaying that information, such as a graph or table in static or
interactive form, i.e. a “data product.”
Polish. After creating initial versions of data
products, research teams often spend time refining or polishing them, by
adding or editing titles, labels, and notations and by working with
colors and shapes to highlight key points.
Narrate. Writing a narrative to accompany the
data products involves, at a minimum, pairing a data product with its
related research question, describing how best to interpret the data
product, and explaining the ways in which the data product helps answer
the research question.
For your learning ML Learning Lab 1 Badge, you will have an
opportunity to create a simple “data product” designed to illustrate
insights some insights gained from your model and ideally highlight an
“action step” that can be taken to act upon your findings.
👉 Your Turn ⤵
For now, we will wrap up this case study by converting our work into
a webpage that can be used to communicate your learning and demonstrate
some of your new R skills. To do so, you will need to “knit” your
document by clicking the button in the menu bar at the the top of this
file. This will do two things; it will:
check through all your code for any errors; and,
create a file in your directory that you can use to share you
work through Posit
Cloud (see screenshot example below to publish), RPubs , GitHub Pages, Quarto Pub, or any other
methods.
Note: Before knitting, make sure you change the
author:
name in the YAML header at the top of this document
so you can take credit for your hard work!

Congratulations - you’ve completed the first machine learning case
study!
LS0tDQp0aXRsZTogIkxhYiAxIENhc2UgU3R1ZHkiDQphdXRob3I6ICJNaWNoYWVsIFNjaHJhbSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwnJUIgJWUsICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA3Mg0KcmVzb3VyY2VfZmlsZXM6DQotIGltZy90aWR5bW9kZWxzLnBuZw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSkNCmBgYA0KDQojIyAxLiBQUkVQQVJFDQoNCkVhY2ggbWFjaGluZSBsZWFybmluZyAiY2FzZSBzdHVkeSIgaXMgZGVzaWduZWQgdG8gaWxsdXN0cmF0ZSBob3cgbWFjaGluZQ0KbGVhcm5pbmcgbWV0aG9kcyBhbmQgdGVjaG5pcXVlcyBjYW4gYmUgYXBwbGllZCB0byBhZGRyZXNzIGEgcmVzZWFyY2gNCnF1ZXN0aW9uIG9mIGludGVyZXN0LCBjcmVhdGUgdXNlZnVsIGRhdGEgcHJvZHVjdHMsIGFuZCBjb25kdWN0DQpyZXByb2R1Y2libGUgcmVzZWFyY2guIEVhY2ggY2FzZSBzdHVkeSBpcyBzdHJ1Y3R1cmVkIGFyb3VuZCBhIGJhc2ljDQphbmFseXRpY3Mgd29ya2Zsb3cgbW9kZWxlZCBhZnRlciB0aGUgRGF0YS1JbnRlbnNpdmUgUmVzZWFyY2ggV29ya2Zsb3cNCmZyb20gW0xlYXJuaW5nIEFuYWx5dGljcyBHb2VzIHRvIFNjaG9vbF0oIzApIChLcnVtbSBldCBhbC4sIDIwMTgpOg0KDQohW10oaHR0cHM6Ly9zYmtlbGxvZ2cuZ2l0aHViLmlvL2VjaS01ODkvdW5pdC0xL2ltZy93b3JrZmxvdy5wbmcpe2FsdD0iIg0Kd2lkdGg9IjgwJSJ9DQoNCkZpZ3VyZSAyLjIgU3RlcHMgb2YgRGF0YS1JbnRlbnNpdmUgUmVzZWFyY2ggV29ya2Zsb3cNCg0KSW4gdGhlIG92ZXJ2aWV3IHByZXNlbnRhdGlvbiBmb3IgdGhpcyBsYWIsIHdlIGNvbnNpZGVyZWQgZml2ZSBzdGVwcyBpbg0Kb3VyIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBwcm9jZXNzLiBUaG9zZSBzdGVwcyBhcmUgbWlycm9yZWQgaGVyZQ0KaW4gdGhpcyBjYXNlIHN0dWR5LCB3aXRoIHRoZSBhZGRpdGlvbiBvZiBzb21lIG90aGVyIGNvbXBvbmVudHMgb2YgdGhpcw0Kd29ya2Zsb3cuIEZvciBleGFtcGxlLCB0byBoZWxwIHByZXBhcmUgZm9yIGFuYWx5c2lzLCB3ZSdsbCBmaXJzdCB0YWtlIGENCnN0ZXAgYmFjayBhbmQgdGhpbmsgYWJvdXQgaG93IHdlIHdhbnQgdG8gdXNlIG1hY2hpbmUgbGVhcm5pbmcsIGFuZA0KKnByZWRpY3RpbmcqIGlzIGEga2V5IHdvcmQuIE1hbnkgc2Nob2xhcnMgaGF2ZSBmb2N1c2VkIG9uIHByZWRpY3RpbmcNCnN0dWRlbnRzIHdobyBhcmUgKmF0LXJpc2sqOiBvZiBkcm9wcGluZyBhIGNvdXJzZSBvciBub3Qgc3VjY2VlZGluZyBpbg0KaXQuIEluIHRoZSBNTCBMYWIgMSBjYXNlIHN0dWR5IHdpbGwgY292ZXIgdGhlIGZvbGxvd2luZyB3b3JrZmxvdyB0b3BpY3MNCmFzIHdlIGF0dGVtcHQgdG8gZGV2ZWxvcCBvdXIgb3duIG1vZGVsIGZvciBwcmVkaWN0aW5nIHN0dWRlbnQgZHJvcC1vdXQ6DQoNCjEuICAqKlByZXBhcmUqKjogUHJpb3IgdG8gYW5hbHlzaXMsIHdlJ2xsIGxvb2sgYXQgdGhlIGNvbnRleHQgZnJvbSB3aGljaA0KICAgIG91ciBkYXRhIGNhbWUsIGZvcm11bGF0ZSBhIGJhc2ljIHJlc2VhcmNoIHF1ZXN0aW9uLCBhbmQgZ2V0DQogICAgaW50cm9kdWNlZCB0aGUge3RpZHltb2RlbHN9IHBhY2thZ2VzIGZvciBtYWNoaW5lIGxlYXJuaW5nLg0KDQoyLiAgKipXcmFuZ2xlKio6IFdyYW5nbGluZyBkYXRhIGVudGFpbHMgdGhlIHdvcmsgb2YgY2xlYW5pbmcsDQogICAgdHJhbnNmb3JtaW5nLCBhbmQgbWVyZ2luZyBkYXRhLiBJbiBQYXJ0IDIgd2UgZm9jdXMgb24gaW1wb3J0aW5nIENTVg0KICAgIGZpbGVzIGFuZCBtb2RpZnlpbmcgc29tZSBvZiBvdXIgdmFyaWFibGVzLg0KDQozLiAgKipFeHBsb3JlKio6IFdlIHRha2UgYSBxdWljayBsb29rIGF0IG91ciB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgYW5kDQogICAgZG8gc29tZSBiYXNpYyAiZmVhdHVyZSBlbmdpbmVlcmluZyIgYnkgY3JlYXRpbmcgc29tZSBuZXcgdmFyaWFibGVzDQogICAgd2UgdGhpbmsgd2lsbCBiZSBwcmVkaWN0aXZlIG9mIHN0dWRlbnRzIGF0IHJpc2suDQoNCjQuICAqKk1vZGVsOioqIFdlIGRpdmUgZGVlcGVyIGludG8gdGhlIGZpdmUgc3RlcHMgaW4gb3VyIHN1cGVydmlzZWQNCiAgICBtYWNoaW5lIGxlYXJuaW5nIHByb2Nlc3MsIGZvY3VzaW5nIG9uIHRoZSBtZWNoYW5pY3Mgb2YgKiptYWtpbmcNCiAgICBwcmVkaWN0aW9ucyoqLg0KDQo1LiAgKipDb21tdW5pY2F0ZToqKiBUbyB3cmFwIHVwIG91ciBjYXNlIHN0dWR5LCB3ZSdsbCBjcmVhdGUgb3VyIGZpcnN0DQogICAgImRhdGEgcHJvZHVjdCIgYW5kIHNoYXJlIG91ciBhbmFseXNlcyBhbmQgZmluZGluZ3MgYnkgY3JlYXRpbmcgb3VyDQogICAgZmlyc3Qgd2ViIHBhZ2UgdXNpbmcgUiBNYXJrZG93bi4NCg0KIyMjIDFhLiBDb25jZXB0dWFsIEZvY3VzDQoNCkNvbmNlcHR1YWxseSwgd2UgZm9jdXMgb24gcHJlZGljdGlvbiBhbmQgaG93IGl0IGRpZmZlcnMgZnJvbSB0aGUgZ29hbHMNCm9mIGRlc2NyaXB0aW9uIG9yIGV4cGxhbmF0aW9uLiBXZSBoYXZlIHR3byByZWFkaW5ncyBpbiBMYWIgMSB0aGF0DQphY2NvbXBhbnkgdGhpcy4gVGhlIGZpcnN0IHJlYWRpbmcgaW50cm9kdWNlZCBiZWxvdyBmb2N1c2VzIG9uIHRoaXMNCmRpc3RpbmN0aW9uIGJldHdlZW4gcHJlZGljdGlvbiBhbmQgZGVzY3JpcHRpb24gb3IgZXhwbGFuYXRpb24uIEl0IGlzIG9uZQ0Kb2YgdGhlIG1vc3Qgd2lkZWx5LXJlYWQgcGFwZXJzIGluIG1hY2hpbmUgbGVhcm5pbmcgYW5kIGFydGljdWxhdGVzIGhvdw0KbWFjaGluZSBsZWFybmluZyBkaWZmZXJzIGZyb20gb3RoZXIga2luZHMgb2Ygc3RhdGlzdGljYWwgbW9kZWxzLiBCcmVpbWFuDQpkZXNjcmliZXMgdGhlIGRpZmZlcmVuY2UgaW4gdGVybXMgb2YgKmRhdGEgbW9kZWxpbmcqIChtb2RlbHMgZm9yDQpkZXNjcmlwdGlvbiBhbmQgZXhwbGFuYXRpb24pIGFuZCAqYWxnb3JpdGhtaWMgbW9kZWxpbmcqICh3aGF0IHdlIGNhbGwNCnByZWRpY3Rpb24gb3IgbWFjaGluZSBsZWFybmluZyBtb2RlbHMpXCoNCg0KIyMjIyBSZXNlYXJjaCBRdWVzdGlvbg0KDQpUZWNobmljYWxseSwgd2UnbGwgZm9jdXMgb24gdGhlIGNvcmUgcGFydHMgb2YgZG9pbmcgYSBtYWNoaW5lIGxlYXJuaW5nDQphbmFseXNpcyBpbiBSLiBXZSdsbCB1c2UgdGhlIHtbdGlkeW1vZGVsc10oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvKX0NCnNldCBvZiBSIHBhY2thZ2VzIChhZGQtb25zKSB0byBkbyBzby4gSG93ZXZlciwgdG8gaGVscCBhbmNob3Igb3VyDQphbmFseXNpcyBhbmQgcHJvdmlkZSB1cyB3aXRoIHNvbWUgZGlyZWN0aW9uLCB3ZSdsbCBmb2N1cyBvbiB0aGUNCmZvbGxvd2luZyByZXNlYXJjaCBxdWVzdGlvbiBhcyB3ZSBleHBsb3JlIHRoaXMgbmV3Og0KDQo+IEhvdyB3ZWxsIGNhbiB3ZSBwcmVkaWN0IHN0dWRlbnRzIHdobyBhcmUgYXQgcmlzayBvZiBkcm9wcGluZyBhIGNvdXJzZT8NCg0KIyMjIyBSZWFkaW5nOiBTdGF0aXN0aWNhbCBtb2RlbGluZzogVGhlIHR3byBjdWx0dXJlcw0KDQo+IEJyZWltYW4sIEwuICgyMDAxKS4gU3RhdGlzdGljYWwgbW9kZWxpbmc6IFRoZSB0d28gY3VsdHVyZXMgKHdpdGgNCj4gY29tbWVudHMgYW5kIGEgcmVqb2luZGVyIGJ5IHRoZSBhdXRob3IpLiAqU3RhdGlzdGljYWwgU2NpZW5jZSwgMTYqKDMpLA0KPiAxOTktMjMxLg0KPiA8aHR0cHM6Ly9wcm9qZWN0ZXVjbGlkLm9yZy9qb3VybmFscy9zdGF0aXN0aWNhbC1zY2llbmNlL3ZvbHVtZS0xNi9pc3N1ZS0zL1N0YXRpc3RpY2FsLU1vZGVsaW5nLS1UaGUtVHdvLUN1bHR1cmVzLXdpdGgtY29tbWVudHMtYW5kLWEvMTAuMTIxNC9zcy8xMDA5MjEzNzI2LnBkZj4NCg0KKirwn5GJIFlvdXIgVHVybioqICoq4qS1KioNCg0KWW91J2xsIGJlIGFza2VkIHRvIHJlZmxlY3QgbW9yZSBkZWVwbHkgb24gdGhpcyBhcnRpY2xlIGxhdGVyIG9uIChpbiB0aGUNCmJhZGdlIGFjdGl2aXR5KTsgYnV0IGZvciBub3csIG9wZW4gdXAgdGhlIGFydGljbGUgYW5kIHRha2UgcXVpY2sgc2NhbiBvZg0KdGhlIGFydGljbGUgYW5kIG5vdGUgYmVsb3cgYW4gb2JzZXJ2YXRpb24gb3IgcXVlc3Rpb24geW91IGhhdmUgYWJvdXQgdGhlDQphcnRpY2xlLg0KDQotICAgKipPYnNlcnZhdGlvbjoqKg0KDQpBbiBvYnNlcnZhdGlvbiB0aGF0IEkgaGF2ZSBpZGVudGlmaWVkIGFzIGNvbmNlcm5pbmcgaW4gdGhpcyBwdWJsaWNhdGlvbiBpcyB0aGUgYXV0aG9yJ3MgcG9zaXRpb24gb24gcHVibGlzaGVkIHJlc3VsdHMuIFRoZXkgY29uY2x1ZGUgdGhhdCBpdCBpcyBvZnRlbiB0aGUgY2FzZSB0aGF0IHByb2Zlc3Npb25hbHMgcHV0IGxlc3MgZW1waGFzaXMgb24gZGV2ZWxvcGluZyBhIG1vZGVsIHRoYXQgZml0cyB0aGUgcHJvYmxlbSB0aGV5IGFyZSBhdHRlbXB0aW5nIHRvIHNvbHZlIHRoYW4gdGhleSBkbyBvbiBhIG1vZGVsIHRoYXQgaXMgaW4gYXBwZWFyYW5jZSwgZXhwZXJ0bHkgY3JhZnRlZCAoQnJlaW1hbiwgMjAzKS4gSW4gZXNzZW5jZSwgY29tcGxleGl0eSBpcyBmYXZvcmVkIG92ZXIgdXNhYmlsaXR5Lg0KDQojIyMjIFJlYWRpbmc6IFByZWRpY3Rpbmcgc3R1ZGVudHMnIGZpbmFsIGdyYWRlcw0KDQo+IEVzdHJlbGxhZG8sIFIuIEEuLCBGcmVlciwgRS4gQS4sIE1vc3RpcGFrLCBKLiwgUm9zZW5iZXJnLCBKLiBNLiwgJg0KPiBWZWzDoXNxdWV6LCBJLiBDLiAoMjAyMCkuICpEYXRhIHNjaWVuY2UgaW4gZWR1Y2F0aW9uIHVzaW5nIFIqLg0KPiBSb3V0bGVkZ2UgKGMxNCksIFByZWRpY3Rpbmcgc3R1ZGVudHMnIGZpbmFsIGdyYWRlcyB1c2luZyBtYWNoaW5lDQo+IGxlYXJuaW5nIG1ldGhvZHMgd2l0aCBvbmxpbmUgY291cnNlIGRhdGEuDQo+IDxodHRwOi8vd3d3LmRhdGFzY2llbmNlaW5lZHVjYXRpb24uY29tLz4NCg0KUGxlYXNlIHJldmlldyB0aGlzIGNoYXB0ZXIsIGZvY3VzaW5nIG9uIHRoZSBvdmVyYWxsIGdvYWxzIG9mIHRoZQ0KYW5hbHlzaXMgYW5kIGhvdyB0aGUgYW5hbHlzaXMgd2FzIHByZXNlbnRlZCAoZm9jdXNpbmcgb24gcHJlZGljdGlvbnMsDQpyYXRoZXIgdGhhbiB0aGUgd2F5cyB3ZSBtYXkgdHlwaWNhbGx5IGludGVycHJldCBhIHN0YXRpc3RpY2FsDQptb2RlbC0tbGlrZSBtZWFzdXJlcyBvZiBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UpLg0KDQojIyMgMWIuIExvYWQgUGFja2FnZXMNCg0KQXMgaGlnaGxpZ2h0ZWQgaW7CoFtDaGFwdGVyIDYgb2YgRGF0YSBTY2llbmNlIGluIEVkdWNhdGlvbiBVc2luZw0KUl0oaHR0cHM6Ly9kYXRhc2NpZW5jZWluZWR1Y2F0aW9uLmNvbS9jMDYuaHRtbCnCoChEU0lFVVIpLCBvbmUgb2YgdGhlDQpmaXJzdCBzdGVwcyBvZiBldmVyeSB3b3JrZmxvdyBzaG91bGQgYmUgdG8gc2V0IHVwIHlvdXIgIlByb2plY3QiIHdpdGhpbg0KUlN0dWRpby4gUmVjYWxsIHRoYXQ6DQoNCj4gQSAqKlByb2plY3QqKiBpcyB0aGUgaG9tZSBmb3IgYWxsIG9mIHRoZSBmaWxlcywgaW1hZ2VzLCByZXBvcnRzLCBhbmQNCj4gY29kZSB0aGF0IGFyZSB1c2VkIGluIGFueSBnaXZlbiBwcm9qZWN0DQoNClNpbmNlIHdlIGFyZSB3b3JraW5nIGZyb20gYW4gUiBwcm9qZWN0IGNsb25lZCBmcm9tDQpbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vRHJJdGF1bWEtTlUpLCBhIFByb2plY3QgaGFzIGFscmVhZHkgYmVlbiBzZXQNCnVwIGZvciB5b3UgYXMgaW5kaWNhdGVkIGJ5IHRoZSBgLlJwcm9qYCBmaWxlIGluIHlvdXIgbWFpbiBkaXJlY3RvcnkgaW4NCnRoZSBGaWxlcyBwYW5lLiBJbnN0ZWFkLCB3ZSB3aWxsIGZvY3VzIG9uIGdldHRpbmcgb3VyIHByb2plY3Qgc2V0IHVwDQp3aXRoIHRoZSByZXF1aXNpdGUgcGFja2FnZXMgd2UnbGwgbmVlZCBmb3IgYW5hbHlzaXMuDQoNCioqUGFja2FnZXMqKiwgc29tZXRpbWVzIGNhbGxlZCBsaWJyYXJpZXMsIGFyZSBzaGFyZWFibGUgY29sbGVjdGlvbnMgb2YgUg0KY29kZSB0aGF0IGNhbiBjb250YWluIGZ1bmN0aW9ucywgZGF0YSwgYW5kL29yIGRvY3VtZW50YXRpb24gYW5kIGV4dGVuZA0KdGhlIGZ1bmN0aW9uYWxpdHkgb2YgUi4gWW91IGNhbiBhbHdheXMgY2hlY2sgdG8gc2VlIHdoaWNoIHBhY2thZ2VzIGhhdmUNCmFscmVhZHkgYmVlbiBpbnN0YWxsZWQgYW5kIGxvYWRlZCBpbnRvIFJTdHVkaW8gb3IgUlN0dWRpbyBDbG91ZCBieQ0KbG9va2luZyBhdCB0aGUgRmlsZXMsIFBsb3RzLCAmIFBhY2thZ2VzIFBhbmUgaW4gdGhlIGxvd2VyIHJpZ2h0LWhhbmQNCmNvcm5lci4NCg0KVHdvIHBhY2thZ2VzIHdlJ2xsIHVzZSBleHRlbnNpdmVseSB0aHJvdWdob3V0IHRoZXNlIGxhYnMgYXJlIHRoZQ0Ke3RpZHl2ZXJzZX0gYW5kIHt0aWR5bW9kZWxzfSBwYWNrYWdlcy4NCg0KIyMjIyB0aWR5dmVyc2Ug8J+Tpg0KDQohW10oaW1nL3RpZHl2ZXJzZS5wbmcpe3dpZHRoPSIyMCUifQ0KDQpPbmUgcGFja2FnZSB0aGF0IHdlJ2xsIGJlIHVzaW5nIGV4dGVuc2l2ZWx5IHRocm91Z2hvdXQgTEFTRVIgaXMgdGhlDQp7dGlkeXZlcnNlfSBwYWNrYWdlLiBSZWNhbGwgZnJvbSBlYXJsaWVyIHR1dG9yaWFscyB0aGF0IHRoZSB7dGlkeXZlcnNlfQ0KcGFja2FnZSBpcyBhY3R1YWxseSBhIFtjb2xsZWN0aW9uIG9mIFINCnBhY2thZ2VzXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnL3BhY2thZ2VzKSBkZXNpZ25lZCBmb3IgcmVhZGluZywNCndyYW5nbGluZywgYW5kIGV4cGxvcmluZyBkYXRhIGFuZCB3aGljaCBhbGwgc2hhcmUgYW4gdW5kZXJseWluZyBkZXNpZ24NCnBoaWxvc29waHksIGdyYW1tYXIsIGFuZCBkYXRhIHN0cnVjdHVyZXMuIFRoZXNlIHNoYXJlZCBmZWF0dXJlcyBhcmUNCnNvbWV0aW1lcyAidGlkeSBkYXRhIHByaW5jaXBsZXMuIg0KDQpDbGljayB0aGUgZ3JlZW4gYXJyb3cgaW4gdGhlIHJpZ2h0IGNvcm5lciBvZiB0aGUgImNvZGUgY2h1bmsiIHRoYXQNCmZvbGxvd3MgdG8gbG9hZCB0aGUge3RpZHl2ZXJzZX0gbGlicmFyeS4NCg0KYGBge3IgbG9hZC10aWR5dmVyc2V9DQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpgYGANCg0KIyMjIyB0aWR5bW9kZWxzDQoNClshW10oaW1nL3RpZHltb2RlbHMucG5nKXt3aWR0aD0iMjAlIn1dKGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnLykNCg0KVGhlIFt0aWR5bW9kZWxzXShodHRwczovL3d3dy50aWR5bW9kZWxzLm9yZy8pIHBhY2thZ2UgaXMgYQ0KIm1ldGEtcGFja2FnZSIgZm9yIG1vZGVsaW5nIGFuZCBzdGF0aXN0aWNhbCBhbmFseXNpcyB0aGF0IHNoYXJlcyB0aGUNCnVuZGVybHlpbmcgZGVzaWduIHBoaWxvc29waHksIGdyYW1tYXIsIGFuZCBkYXRhIHN0cnVjdHVyZXMgb2YgdGhlDQpbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykuIEl0IGluY2x1ZGVzIGEgY29yZSBzZXQgb2YNCnBhY2thZ2VzIHRoYXQgYXJlIGxvYWRlZCBvbiBzdGFydHVwIGFuZCBjb250YWlucyB0b29scyBmb3I6DQoNCi0gICBkYXRhIHNwbGl0dGluZyBhbmQgcHJlLXByb2Nlc3Npbmc7DQoNCi0gICBtb2RlbCBzZWxlY3Rpb24sIHR1bmluZywgYW5kIGV2YWx1YXRpb247DQoNCi0gICBmZWF0dXJlIHNlbGVjdGlvbiBhbmQgdmFyaWFibGUgaW1wb3J0YW5jZSBlc3RpbWF0aW9uOw0KDQotICAgYXMgd2VsbCBhcyBvdGhlciBmdW5jdGlvbmFsaXR5Lg0KDQojIyMjICoq8J+RiSBZb3VyIFR1cm4qKiAqKuKktSoqDQoNCkluIGFkZGl0aW9uIHRvIHRoZSB7dGlkeW1vZGVsc30gcGFja2FnZSwgd2UnbGwgYWxzbyBiZSB1c2luZyB0aGUNCmxpZ2h0d2VpZ2h0IGJ1dCBoaWdobHkgdXNlZnVsIHtqYW5pdG9yfSBwYWNrYWdlIHRvIGhlbHAgd2l0aCBzb21lIGRhdGENCmNsZWFuaW5nIHRhc2tzLiBVc2UgdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gbG9hZCB0aGVzZSB0d28gcGFja2FnZXM6DQoNCmBgYHtyIGxvYWQtdGlkeW1vZGVsc30NCg0KDQoNCmBgYA0KDQpBcyBhIHRpcCwgcmVtZW1iZXIgdG8gdXNlIHRoZSBgbGlicmFyeSgpYCBmdW5jdGlvbiB0byBsb2FkIHRoZXNlDQpwYWNrYWdlcy4gQWZ0ZXIgeW91J3ZlIGRvbmUgdGhhdCwgY2xpY2sgdGhlIGdyZWVuIGFycm93IHRvIHJ1biB0aGUgY29kZQ0KY2h1bmsuIElmIHlvdSBzZWUgYSBidW5jaCBvZiBtZXNzYWdlcyAobm90IGFueXRoaW5nIGxhYmVsZWQgYXMgYW4NCmVycm9yKSwgeW91IGFyZSBnb29kIHRvIGdvISBUaGVzZSBtZXNzYWdlcyBtZWFuIHRoZSBwYWNrYWdlcyBsb2FkZWQNCmNvcnJlY3RseS4NCg0KIyMgMi4gV1JBTkdMRQ0KDQpJbiBnZW5lcmFsLCBkYXRhIHdyYW5nbGluZyBpbnZvbHZlcyBzb21lIGNvbWJpbmF0aW9uIG9mIGNsZWFuaW5nLA0KcmVzaGFwaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBtZXJnaW5nIGRhdGEgKFdpY2toYW0gJiBHcm9sZW11bmQsIDIwMTcpLg0KVGhlIGltcG9ydGFuY2Ugb2YgZGF0YSB3cmFuZ2xpbmcgaXMgZGlmZmljdWx0IHRvIG92ZXJzdGF0ZSwgYXMgaXQNCmludm9sdmVzIHRoZSBpbml0aWFsIHN0ZXBzIG9mIGdvaW5nIGZyb20gcmF3IGRhdGEgdG8gYSBkYXRhc2V0IHRoYXQgY2FuDQpiZSBleHBsb3JlZCBhbmQgbW9kZWxlZCAoS3J1bW0gZXQgYWwsIDIwMTgpLiBJbiBQYXJ0IDIsIHdlIGZvY3VzIG9uIHRoZQ0KdGhlIGZvbGxvd2luZyB3cmFuZ2xpbmcgcHJvY2Vzc2VzIHRvOg0KDQoxLiAgKipJbXBvcnRpbmcgYW5kIEluc3BlY3RpbmcgRGF0YSoqLiBJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgInJlYWQiDQogICAgaW4gb3VyIENTViBkYXRhIGZpbGUgYW5kIHRha2UgYSBxdWljayBsb29rIGF0IHdoYXQgb3VyIGZpbGUNCiAgICBjb250YWlucy4NCg0KMi4gICoqTXV0YXRlIFZhcmlhYmxlcyoqLiBXZSB1c2UgdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGENCiAgICBkaWNob3RvbW91cyB2YXJpYWJsZSBmb3Igd2hldGhlciBvciBub3QgdGhlIHN0dWRlbnQgd2l0aGRyZXcgZnJvbQ0KICAgIHRoZSBjb3Vyc2UuDQoNCiMjIyAxYS4gSW1wb3J0IGFuZCBJbnNwZWN0IERhdGENCg0KRm9yIGxlYXJuaW5nIGxhYnMgMS0zLCB3ZSdsbCBiZSB1c2luZyBhIHdpZGVseS11c2VkIGRhdGEgc2V0IGluIHRoZQ0KbGVhcm5pbmcgYW5hbHl0aWNzIGZpZWxkOiB0aGUgW09wZW4gVW5pdmVyc2l0eSBMZWFybmluZyBBbmFseXRpY3MNCkRhdGFzZXQgKE9VTEFEKV0oaHR0cHM6Ly9hbmFseXNlLmttaS5vcGVuLmFjLnVrL29wZW5fZGF0YXNldCkuIFRoZSBPVUxBRA0Kd2FzIGNyZWF0ZWQgYnkgbGVhcm5pbmcgYW5hbHl0aWNzIHJlc2VhcmNoZXJzIGF0IHRoZSBVbml0ZWQNCktpbmdkb20tYmFzZWQgT3BlbiBVbml2ZXJzaXR5LiBJdCBpbmNsdWRlcyBkYXRhIGZyb20gcG9zdC1zZWNvbmRhcnkNCmxlYXJuZXJzIHBhcnRpY2lwYXRpb24gaW4gb25lIG9mIHNldmVyYWwgTWFzc2l2ZSBPcGVuIE9ubGluZSBDb3Vyc2VzDQooY2FsbGVkICptb2R1bGVzKiBpbiB0aGUgT1VMQUQpLg0KDQo+IEt1emlsZWssIEouLCBIbG9zdGEsIE0uLCAmIFpkcmFoYWwsIFouICgyMDE3KS4gT3BlbiB1bml2ZXJzaXR5DQo+IGxlYXJuaW5nIGFuYWx5dGljcyBkYXRhc2V0LiAqU2NpZW50aWZpYyBEYXRhLCA0KCoxKSwgMS04Lg0KPiA8aHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zZGF0YTIwMTcxNzE+DQoNCioqQWJzdHJhY3QqKg0KDQpMZWFybmluZyBBbmFseXRpY3MgZm9jdXNlcyBvbiB0aGUgY29sbGVjdGlvbiBhbmQgYW5hbHlzaXMgb2YgbGVhcm5lcnMnDQpkYXRhIHRvIGltcHJvdmUgdGhlaXIgbGVhcm5pbmcgZXhwZXJpZW5jZSBieSBwcm92aWRpbmcgaW5mb3JtZWQgZ3VpZGFuY2UNCmFuZCB0byBvcHRpbWlzZSBsZWFybmluZyBtYXRlcmlhbHMuIFRvIHN1cHBvcnQgdGhlIHJlc2VhcmNoIGluIHRoaXMgYXJlYQ0Kd2UgaGF2ZSBkZXZlbG9wZWQgYSBkYXRhc2V0LCBjb250YWluaW5nIGRhdGEgZnJvbSBjb3Vyc2VzIHByZXNlbnRlZCBhdA0KdGhlIE9wZW4gVW5pdmVyc2l0eSAoT1UpLiBXaGF0IG1ha2VzIHRoZSBkYXRhc2V0IHVuaXF1ZSBpcyB0aGUgZmFjdCB0aGF0DQppdCBjb250YWlucyBkZW1vZ3JhcGhpYyBkYXRhIHRvZ2V0aGVyIHdpdGggYWdncmVnYXRlZCBjbGlja3N0cmVhbSBkYXRhDQpvZiBzdHVkZW50cycgaW50ZXJhY3Rpb25zIGluIHRoZSBWaXJ0dWFsIExlYXJuaW5nIEVudmlyb25tZW50IChWTEUpLg0KVGhpcyBlbmFibGVzIHRoZSBhbmFseXNpcyBvZiBzdHVkZW50IGJlaGF2aW9yLCByZXByZXNlbnRlZCBieSB0aGVpcg0KYWN0aW9ucy4gVGhlIGRhdGFzZXQgY29udGFpbnMgdGhlIGluZm9ybWF0aW9uIGFib3V0IDIyIGNvdXJzZXMsIDMyLDU5Mw0Kc3R1ZGVudHMsIHRoZWlyIGFzc2Vzc21lbnQgcmVzdWx0cywgYW5kIGxvZ3Mgb2YgdGhlaXIgaW50ZXJhY3Rpb25zIHdpdGgNCnRoZSBWTEUgcmVwcmVzZW50ZWQgYnkgZGFpbHkgc3VtbWFyaWVzIG9mIHN0dWRlbnQgY2xpY2tzICgxMCw2NTUsMjgwDQplbnRyaWVzKS4gVGhlIGRhdGFzZXQgaXMgZnJlZWx5IGF2YWlsYWJsZSBhdA0KPGh0dHBzOi8vYW5hbHlzZS5rbWkub3Blbi5hYy51ay9vcGVuX2RhdGFzZXQ+IHVuZGVyIGEgQ0MtQlkgNC4wIGxpY2Vuc2UuDQoNCiMjIyMgKirwn5GJIFlvdXIgVHVybioqICoq4qS1KioNCg0KWW91IGRvbid0IG5lZWQgdG8gcmVhZCB0aGUgZW50aXJlIGFydGljbGUgeWV0LCBidXQgcGxlYXNlIG9wZW4gdGhpcw0KYXJ0aWNsZSwgc2NhbiB0aGUgc2VjdGlvbnMsIGFuZCB3cml0ZSBkb3duIHR3byB0aGluZ3MgeW91IG5vdGljZSBvcg0Kd29uZGVyIGFib3V0IHRoZSBkYXRhc2V0Lg0KDQoxLiAgT25lIG9mIHRoZSBmaXJzdCB0aG91Z2h0cyBJIGhhZCBoYWQsIGJlaW5nIGluIGhpZ2hlciBlZHVjYXRpb24gYW5kIGxvb2tpbmcgdG8gcmVzZWFyY2ggd2l0aCBzdHVkZW50IGRhdGEgaXMgdGhlIG5hdHVyYWwgY29uY2VybiBvZiBGRVJQQSB2aW9sYXRpb25zIHdpdGggcmVzcGVjdCB0byBwZXJzb25hbGx5IGlkZW50aWZpYWJsZSBpbmZvcm1hdGlvbi4gT1VMQUQgYXBwZWFycyB0byBoYXZlIGEgcm9idXN0IHN5c3RlbSBvZiBub3Qgb25seSBpbmZvcm1pbmcgc3R1ZGVudHMgb2YgdGhlIHVzZSBvZiBkYXRhLCBidXQgZWZmZWN0aXZlbHkgc2NydWJiaW5nIHRoZSBkYXRhIGZvciBhbnkgaW5mb3JtYXRpb24gd2hpY2ggY291bGQgYmUgaGFybWZ1bCB0byB0aGUgc3R1ZGVudCBpZiByZWxlYXNlZC4NCg0KMi4gIE9mIHRoZSB0aHJlZSBkaWZmZXJlbnQgZGF0YSB0eXBlcyB1c2VkIGluIHRoZSBPVUxBRCBSZXNlYXJjaCAoRGVtb2dyYXBoaWMsIFBlcmZvcm1hbmNlLCBhbmQgTGVhcm5pbmcgQmVoYXZpb3IpLCBJIGFtIG1vc3QgaW50ZXJlc3RlZCB0byBzZWUgd2hhdCBpcyBpbmNsdWRlZCBpbiBsZWFybmluZyBiZWhhdmlvciBpbiBvcmRlciB0byB0cmFuc2xhdGUgdGhhdCBpbmZvcm1hdGlvbiBmb3IgbXkgb3duIHVzZS4NCg0KIyMjIyBSZWFkIENTViBEYXRhIEZpbGUNCg0KVGhlIGRhdGEgY2FuIGJlIGRvd25sb2FkZWQgYXQgdGhlIGFib3ZlIGxpbms7IGhvd2V2ZXIsIGZvciBvdXIgcHVycG9zZXMsDQp0aGV5IGFyZSBhbHJlYWR5IGRvd25sb2FkZWQgdG8gdGhlIGBkYXRhYCBzdWItZm9sZGVyLg0KDQpXZSdsbCB1c2UgdGhlIGByZWFkX2NzdigpYCBmdW5jdGlvbiB0byBsb2FkIHRoZSBmaWxlcyAtLSB0d28gaW4gdG90YWwsDQp3aXRoIGRhdGEgb24gc3R1ZGVudHMgYW5kIGFzc2Vzc21lbnRzLiBOb3RlOiB3ZSBoYXZlIGRvbmUgc29tZSBtaW5pbWFsDQpwcm9jZXNzaW5nIG9mIHRoZXNlIGZpbGVzIHRvIG1ha2UgZ2V0dGluZyB1cyBzdGFydGVkIGVhc2llci4gSWYgeW91J3JlDQppbnRlcmVzdGVkIGluIHdoYXQgd2UndmUgZG9uZSwgY2hlY2sgb3V0IHRoZSBgb3VsYWQuUmAgZmlsZSBpbiB0aGUNCmBsYWItMWAgZm9sZGVyLg0KDQpGb3Igbm93LCBwbGVhc2UgcmVhZCBpbiB0aGUgYG91bGFkLXN0dWRlbnRzLmNzdmAgZmlsZS4gVXNlIHRoZQ0KYHJlYWRfY3N2KClgIGZ1bmN0aW9uIHRvIGRvIHRoaXMsIHBheWluZyBhdHRlbnRpb24gdG8gd2hlcmUgdGhvc2UgZmlsZXMNCmFyZSBsb2NhdGVkIHJlbGF0aXZlIHRvIHRoaXMgY2FzZSBzdHVkeSBmaWxlLg0KDQpgYGB7cn0NCnN0dWRlbnRzIDwtIHJlYWRfY3N2KCJvdWxhZC1zdHVkZW50cy5jc3YiKQ0KYGBgDQoNCllvdSBjYW4gc2VlIGEgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGENCltoZXJlXShodHRwczovL2FuYWx5c2Uua21pLm9wZW4uYWMudWsvb3Blbl9kYXRhc2V0I2Rlc2NyaXB0aW9uKS4gVGhlDQpzdHVkZW50cyBmaWxlIGluY2x1ZGVzIHRocmVlIGZpbGVzIGpvaW5lZCB0b2dldGhlcjogc3R1ZGVudEluZm8sDQpjb3Vyc2VzLCBhbmQgc3R1ZGVudFJlZ2lzdHJhdGlvbi4gVGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEgZGVzY3JpcHRpb24gdG8NCmdldCBhIHNlbnNlIGZvciB3aGF0IHZhcmlhYmxlcyBhcmUgaW4gd2hpY2ggZGF0YSBmcmFtZS4NCg0KIyMjIyAqKvCfkYkgWW91ciBUdXJuKiogKiripLUqKg0KDQojIyMjIEluc3BlY3QgRGF0YQ0KDQpJbiB0aGUgY2h1bmsgYmVsb3csIGV4YW1pbmUgdGhlIGRhdGEgc2V0IHVzaW5nIGEgZnVuY3Rpb24gb3IgbWVhbnMgb2YNCnlvdXIgY2hvaWNlIChzdWNoIGFzIGp1c3QgKnByaW50aW5nKiB0aGUgZGF0YSBzZXQgYnkgdHlwaW5nIGl0cyBuYW1lIG9yDQp1c2luZyB0aGUgYGdsaW1wc2UoKWAgZnVuY3Rpb24pLiBEbyB0aGlzIGluIHRoZSBjb2RlIGNodW5rIGJlbG93ISBOb3RlDQppdHMgZGltZW5zaW9ucyAtLS0gZXNwZWNpYWxseSBob3cgbWFueSByb3dzIGl0IGhhcyENCg0KYGBge3J9DQpnbGltcHNlKCJvdWxhZC1zdHVkZW50cy5jc3YiKQ0KYGBgDQoNCldyaXRlIGRvd24gYSBmZXcgb2JzZXJ2YXRpb25zIGFmdGVyIGluc3BlY3RpbmcgdGhlIGRhdGE6DQoNCi0gIDE1IHZhcmlhYmxlcyAgDQoNCi0gICAzMjU5MyByZWNvcmRzDQoNCi0gICB2YXJpYWJsZXMgaW5jbHVkZSBtb3N0bHkgZGVtb2dyYXBoaWMgZGF0YQ0KDQojIyMgMmIuICJNdXRhdGUiIFZhcmlhYmxlcw0KDQpXZSdyZSBnb2luZyB0byBkbyBhIGZldyBtb3JlIHN0ZXBzIHJlbGF0ZWQgdG8gZGF0YSB3cmFuZ2xpbmcgaGVyZSwNCm5vdGluZyB3ZSBjb3VsZCBhbHNvIGRvIHRoZXNlIGF0IGxhdGVyIHN0YWdlcyBvZiBvdXIgcHJvY2VzcyAobmFtZWx5LCBpbg0KdGhlIGZlYXR1cmUgZW5naW5lZXJpbmcgc3RhZ2UpLg0KDQpGaXJzdCwgc2luY2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gZGV2ZWxvcGluZyBhIG1vZGVsIHRoYXQgY2FuIHByZWRpY3QNCndoZXRoZXIgYSBzdHVkZW50IGlzIGF0IHJpc2sgb2YgZHJvcHBpbmcgYSBjb3Vyc2UsIGFuZCBzbyB3ZSBjYW4NCmludGVydmVuZSBiZWZvcmUgdGhhdCBoYXBwZW5zLCB3ZSBuZWVkIGFuIG91dGNvbWUgdmFyaWFibGUgdGhhdCBsZXQncyB1cw0Ka25vdyBpZiB0aGV5IGhhdmUgcGFzc2VkLg0KDQpUbyBjcmVhdGUgdGhpcyB2YXJpYWJsZSwgbGV0J3MgdXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhDQpkaWNob3RvbW91cyB2YXJpYWJsZSBmb3Igd2hldGhlciBvciBub3QgdGhlIHN0dWRlbnQgd2l0aGRyZXcgZnJvbSB0aGUNCmNvdXJzZS4gSGVyZSdzIGEgd2F5IHdlIGNhbiBkbyB0aGlzLCB1c2luZyBgaWZfZWxzZSgpYCBhbmQNCmBhcy5mYWN0b3IoKWAuIFRoaXMgd2lsbCBiZSBvdXIgKm91dGNvbWUqIHZhcmlhYmxlLCBvciB0aGUgcHJlZGljdGVkDQp2YXJpYWJsZS4NCg0KYGBge3J9DQpzdHVkZW50cyA8LSBzdHVkZW50cyAlPiUNCiAgICBtdXRhdGUocGFzcyA9IGlmZWxzZShmaW5hbF9yZXN1bHQgPT0gIlBhc3MiLCAxLCAwKSkgJT4lICMgY3JlYXRlcyBhIG5ldyB2YXJpYWJsZSBuYW1lZCAicGFzcyIgYW5kIGEgZHVtbXkgY29kZSBvZiAxIGlmIHZhbHVlIG9mIGZpbmFsX3Jlc3VsdCBlcXVhbHMgInBhc3MiIGFuZCAwIGlmIG5vdA0KICAgIG11dGF0ZShwYXNzID0gYXMuZmFjdG9yKHBhc3MpKSAjIG1ha2VzIHRoZSB2YXJpYWJsZSBhIGZhY3RvciwgaGVscGluZyBsYXRlciBzdGVwcw0KDQpgYGANCg0KKipOb3RlKio6IFRoZSBbYG11dGF0ZSgpYA0KZnVuY3Rpb25dKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbXV0YXRlLmh0bWwjOn46dGV4dD1tdXRhdGUoKSUyMGNyZWF0ZXMlMjBuZXclMjBjb2x1bW5zLHNldHRpbmclMjB0aGVpciUyMHZhbHVlJTIwdG8lMjBOVUxMJTIwKS4pDQppcyBhIGNyaXRpY2FsIGZ1bmN0aW9uIHRvIGxlYXJuIGFuZCBpcyB1c2VkIHRvIGNyZWF0ZSBuZXcgY29sdW1ucyB0aGF0DQphcmUgZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcy4gSXQgY2FuIGFsc28gbW9kaWZ5IChpZiB0aGUgbmFtZSBpcw0KdGhlIHNhbWUgYXMgYW4gZXhpc3RpbmcgY29sdW1uKSBhbmQgZGVsZXRlIGNvbHVtbnMgKGJ5IHNldHRpbmcgdGhlaXINCnZhbHVlIHRvIGBOVUxMYCkuDQoNCk5leHQsIGxldCdzIGRvIHNvbWV0aGluZyBzaW1pbGFyIGZvciB3aGV0aGVyIGEgc3R1ZGVudCBpZGVudGlmaWVzIGFzDQpoYXZpbmcgYSBkaXNhYmlsaXR5LiBJbiB0aGlzIGNhc2UsIHRoZXJlIGFyZSBvbmx5IHR3byB2YWx1ZXMgZm9yIHRoZQ0KZGlzYWJpbGl0eSB2YXJpYWJsZSwgc28gd2UgY2FuIHNpbXBseSBjb252ZXJ0IGl0IGRpcmVjdGx5IHRvIGEgZmFjdG9yLg0KTG9vayBhdCB0aGUgY29kZSB5b3UgdXNlZCBhYm92ZSwgKm1vZGlmeWluZyogaXQgZm9yIHRoZSBkaXNhYmlsaXR5DQp2YXJpYWJsZS4gVGhpcyB3aWxsIGJlIGFuICppbmRlcGVuZGVudCB2YXJpYWJsZSosIG9yIGEgcHJlZGljdG9yDQp2YXJpYWJsZS4NCg0KYGBge3J9DQpzdHVkZW50cyA8LSBzdHVkZW50cyAlPiUgDQogICAgbXV0YXRlKGRpc2FiaWxpdHkgPSBhcy5mYWN0b3IoZGlzYWJpbGl0eSkpDQpgYGANCg0KIyMjIyAqKvCfkYkgWW91ciBUdXJuKiogKiripLUqKg0KDQpJbiB0aGUgY2h1bmsgYmVsb3csIHVzZSB0aGUgYHZpZXcoKWAgZnVuY3Rpb24gdG8gbWFudWFsbHkgY2hlY2sgYW5kIHNlZQ0KaWYgb3VyIG5ldyB2YXJpYWJsZSBoYXMgaW5kZWVkIGJlZW4gYWRkZWQgdG8gb3VyIGRhdGEgZnJhbWUuDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KdmlldygiZGlzYWJpbGl0eSIpDQpgYGANCg0KV3JpdGUgZG93biBhIGZldyBvYnNlcnZhdGlvbnMgYWZ0ZXIgaW5zcGVjdGluZyB0aGUgZGF0YToNCg0KRm9yIG1lIHRoaXMgb3BlbnMgdXAgYSAyWDIgdGFibGUgd2l0aCBkaXNhYmlsaXR5IGFzIGFuICd4JyB2YXJpYWJsZSB2YWx1ZS4gSSBob25lc3RseSBhbSBub3Qgc3VyZSBpZiBJIGFtIGRvaW5nIHRoaXMgY29ycmVjdGx5Lg0KDQoqKk5vdGUqKjogVGhlIFtgbXV0YXRlKClgDQpmdW5jdGlvbl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tdXRhdGUuaHRtbCM6fjp0ZXh0PW11dGF0ZSgpJTIwY3JlYXRlcyUyMG5ldyUyMGNvbHVtbnMsc2V0dGluZyUyMHRoZWlyJTIwdmFsdWUlMjB0byUyME5VTEwlMjApLikNCmlzIGEgY3JpdGljYWwgZnVuY3Rpb24gdG8gbGVhcm4gYW5kIGlzIHVzZWQgdG8gY3JlYXRlIG5ldyBjb2x1bW5zIHRoYXQNCmFyZSBmdW5jdGlvbnMgb2YgZXhpc3RpbmcgdmFyaWFibGVzLiBJdCBjYW4gYWxzbyBtb2RpZnkgKGlmIHRoZSBuYW1lIGlzDQp0aGUgc2FtZSBhcyBhbiBleGlzdGluZyBjb2x1bW4pIGFuZCBkZWxldGUgY29sdW1ucyAoYnkgc2V0dGluZyB0aGVpcg0KdmFsdWUgdG8gYE5VTExgKS4NCg0KTmV4dCwgbGV0J3MgZG8gc29tZXRoaW5nIHNpbWlsYXIgZm9yIHdoZXRoZXIgYSBzdHVkZW50IGlkZW50aWZpZXMgYXMNCmhhdmluZyBhIGRpc2FiaWxpdHkuIEluIHRoaXMgY2FzZSwgdGhlcmUgYXJlIG9ubHkgdHdvIHZhbHVlcyBmb3IgdGhlDQpkaXNhYmlsaXR5IHZhcmlhYmxlLCBzbyB3ZSBjYW4gc2ltcGx5IGNvbnZlcnQgaXQgZGlyZWN0bHkgdG8gYSBmYWN0b3IuDQpMb29rIGF0IHRoZSBjb2RlIHlvdSB1c2VkIGFib3ZlLCAqbW9kaWZ5aW5nKiBpdCBmb3IgdGhlIGRpc2FiaWxpdHkNCnZhcmlhYmxlLiBUaGlzIHdpbGwgYmUgYW4gKmluZGVwZW5kZW50IHZhcmlhYmxlKiwgb3IgYSBwcmVkaWN0b3INCnZhcmlhYmxlLg0KDQpgYGB7cn0NCnN0dWRlbnRzIDwtIHN0dWRlbnRzICU+JSANCiAgICBtdXRhdGUoZGlzYWJpbGl0eSA9IGFzLmZhY3RvcihkaXNhYmlsaXR5KSkNCmBgYA0KDQojIyAzLiBFWFBMT1JFDQoNCkFzIG5vdGVkIGJ5IEtydW1tIGV0IGFsLsKgKDIwMTgpLCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIG9mdGVuDQppbnZvbHZlcyBzb21lIGNvbWJpbmF0aW9uIG9mIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgKmZlYXR1cmUNCmVuZ2luZWVyaW5nKi4gSW4gUGFydCAzLCB3ZSB3aWxsIGNyZWF0ZSBhIHF1aWNrIHZpc3VhbGl6YXRpb24gdG8gaGVscCB1cw0Kc3BvdCBhbnkgcG90ZW50aWFsIGlzc3VlcyB3aXRoIG91ciBkYXRhIGFuZCBlbmdpbmVlciBuZXcgcHJlZGljdGl2ZQ0KdmFyaWFibGVzIG9yICJmZWF0dXJlcyIgdGhhdCB3ZSB3aWxsIHVzZSBpbiBvdXIgcHJlZGljdGl2ZSBtb2RlbHMuDQpTcGVjaWZpY2FsbHksIGluIFBhcnQgMyB3ZSB3aWxsOg0KDQoxLiAgKipFeGFtaW5lIE91dGNvbWVzKiogYnkgdGFraW5nIGEgcXVpY2sgYGNvdW50KClgIG9mIHRoZSBudW1iZXIgb2YNCiAgICBzdHVkZW50cyBhbmQgdGhlIG51bWJlciBvZiBzcGVjaWZpYyBvZmZlcmluZ3Mgb2YgZWFjaCBjb3Vyc2UgbW9kdWxlLg0KDQoyLiAgKipFbmdpbmVlciBQcmVkaWN0b3JzKiogYnkgY3JlYXRpbmcgb25lIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlDQogICAgYmFzZWQgb24gYSBtZWFzdXJlIG9mIHNvY2lvZWNvbm9taWMgcmVzb3VyY2VzLS10aGUgaW5kZXggb2YgbXVsdGlwbGUNCiAgICBkZXByYXZpdHkgdmFyaWFibGUuDQoNCiMjIyAzYS4gRXhhbWluZSBWYXJpYWJsZXMNCg0KQSB1c2VmdWwgZnVuY3Rpb24gZm9yIGV4cGxvcmluZyBkYXRhIGlzIGBjb3VudCgpYDsgaXQgZG9lcyB3aGF0IGl0DQpzb3VuZHMgbGlrZSEgSXQgY291bnRzIGhvdyBtYW55IHRpbWVzIHZhbHVlcyBmb3IgYSB2YXJpYWJsZSBhcHBlYXIuDQoNClJlZmVycmluZyB0byB0aGUgW2RhdGENCmRlc2NyaXB0aW9uXShodHRwczovL2FuYWx5c2Uua21pLm9wZW4uYWMudWsvb3Blbl9kYXRhc2V0I2Rlc2NyaXB0aW9uKSwNCmluIHRoZSBjaHVuayBiZWxvdywgY291bnQgdGhlIG51bWJlciBvZiBzdHVkZW50cy4gQWxzbywgY291bnQgdGhlIG51bWJlcg0Kb2YgY291cnNlcyAobW9kdWxlcykgYW5kIHNwZWNpZmljIG9mZmVyaW5ncyAoYXMgbW9kdWxlcyBjYW4gYmUgb2ZmZXJlZA0KbXVsdGlwbGUgdGltZXMgcGVyIHllYXIpLiBMZWFybiBtb3JlIGFib3V0IGBjb3VudCgpYA0KW2hlcmVdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvY291bnQuaHRtbCkuDQoNCmBgYHtyfQ0Kc3R1ZGVudHMgJT4lIA0KICAgIGNvdW50KGlkX3N0dWRlbnQpICMgdGhpcyBtYW55IHN0dWRlbnRzDQoNCnN0dWRlbnRzICU+JSANCiAgICBjb3VudChjb2RlX21vZHVsZSwgY29kZV9wcmVzZW50YXRpb24pICMgdGhpcyBtYW55IG9mZmVyaW5ncw0KYGBgDQoNCiMjIyAzYi4gRmVhdHVyZSBFbmdpbmVlcmluZw0KDQpBcyBkZWZpbmVkIGJ5IEtydW1tLCBNZWFucywgYW5kIEJpZW5rb3dza2kgKDIwMTgpIGluICpMZWFybmluZyBBbmFseXRpY3MNCkdvZXMgdG8gU2Nob29sKjoNCg0KPiAqKkZlYXR1cmUgZW5naW5lZXJpbmcqKiBpcyB0aGUgcHJvY2VzcyBvZiBjcmVhdGluZyBuZXcgdmFyaWFibGVzDQo+IHdpdGhpbiBhIGRhdGFzZXQsIHdoaWNoIGdvZXMgYWJvdmUgYW5kIGJleW9uZCB0aGUgd29yayBvZiByZWNvZGluZyBhbmQNCj4gcmVzY2FsaW5nIHZhcmlhYmxlcy4NCg0KVGhlIGF1dGhvcnMgbm90ZSB0aGF0IGZlYXR1cmUgZW5naW5lZXJpbmcgZHJhd3Mgb24gc3Vic3RhbnRpdmUga25vd2xlZGdlDQpmcm9tIHRoZW9yeSBvciBwcmFjdGljZSwgZXhwZXJpZW5jZSB3aXRoIGEgcGFydGljdWxhciBkYXRhIHN5c3RlbSwgYW5kDQpnZW5lcmFsIGV4cGVyaWVuY2UgaW4gZGF0YS1pbnRlbnNpdmUgcmVzZWFyY2guIE1vcmVvdmVyLCB0aGVzZSBmZWF0dXJlcw0KY2FuIGJlIHVzZWQgbm90IG9ubHkgaW4gbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIGJ1dCBhbHNvIGluDQp2aXN1YWxpemF0aW9ucyBhbmQgdGFibGVzIGNvbXByaXNpbmcgZGVzY3JpcHRpdmUgc3RhdGlzdGljcy4NCg0KVGhvdWdoIG5vdCBvZnRlbiBkaXNjdXNzZWQsIGZlYXR1cmUgZW5naW5lZXJpbmcgaXMgYW4gaW1wb3J0YW50IGVsZW1lbnQNCm9mIGRhdGEtaW50ZW5zaXZlIHJlc2VhcmNoIHRoYXQgY2FuIGdlbmVyYXRlIG5ldyBpbnNpZ2h0cyBhbmQgaW1wcm92ZQ0KcHJlZGljdGl2ZSBtb2RlbHMuIFlvdSBjYW4gcmVhZCBtb3JlIGFib3V0IGZlYXR1cmUgZW5naW5lZXJpbmcNCltoZXJlXShodHRwczovL3d3dy50bXdyLm9yZy9yZWNpcGVzLmh0bWwpLg0KDQojIyMjIFN0dWRlbnQgU29jaW9lY29ub21pYyBJbmRleA0KDQpGb3Igb3VyIGZpcnN0IGxhYiwgd2UnbGwgZW5nYWdlIGluIGEgdmVyeSBiYXNpYyBmZWF0dXJlIGVuZ2luZWVyaW5nDQpzdGVwLCB0aG91Z2ggd2UnbGwgZG8gdGhpcyAqbXVjaCogbW9yZSBpbiB0aGUgbmV4dCBsZWFybmluZyBsYWIuDQoNClRvIGRvIGZlYXR1cmUgZW5naW5lZXJpbmcsIGxldCdzIGNyZWF0ZSBvbmUgbW9yZSBwcmVkaWN0b3IgdmFyaWFibGUNCmJhc2VkIG9uIGEgbWVhc3VyZSBvZiBzb2Npb2Vjb25vbWljIHJlc291cmNlcy0tdGhlIGluZGV4IG9mIG11bHRpcGxlDQpkZXByYXZpdHkgdmFyaWFibGUuIFRoZSBwcm9jZXNzIHdlIHRha2UgaGVyZSBpcyB0byB0dXJuIHRoaXMgdmFyaWFibGUNCnRoYXQgaXMgYSBjaGFyYWN0ZXIgc3RyaW5nIGludG8gYSBudW1iZXIgYnkgY3JlYXRpbmcgYSBmYWN0b3IgYW5kIHRoZW4NCipjb2VyY2luZyogaXQgdG8gYW4gaW50ZWdlci4NCg0KIyMjIyAqKvCfkYkgWW91ciBUdXJuKiogKiripLUqKg0KDQpQbGVhc2UgcmVwbGFjZSB0aGUgXF9cX1xfXF8gdmFsdWVzIGluIHRoZSBjb2RlIGJlbG93IHdpdGggdGhlIGNvcnJlY3QNCnZhcmlhYmxlLg0KDQpgYGB7cn0NCnN0dWRlbnRzIDwtIHN0dWRlbnRzICU+JSANCiAgICBtdXRhdGUoaW1kX2JhbmQgPSBmYWN0b3IoaW1kX2JhbmQsIGxldmVscyA9IGMoIjAtMTAlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEwLTIwJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMC0zMCUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMzAtNDAlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjQwLTUwJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1MC02MCUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNjAtNzAlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjcwLTgwJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI4MC05MCUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiOTAtMTAwJSIpKSkgJT4lICMgdGhpcyBjcmVhdGVzIGEgZmFjdG9yIHdpdGggb3JkZXJlZCBsZXZlbHMNCiAgICBtdXRhdGUoaW1kX2JhbmQgPSBhcy5pbnRlZ2VyKGltZF9iYW5kKSkgIyB0aGlzIGNoYW5nZXMgdGhlIGxldmVscyBpbnRvIGludGVnZXJzIGJhc2VkIG9uIHRoZSBvcmRlciBvZiB0aGUgZmFjdG9yIGxldmVscw0KDQpzdHVkZW50cw0KYGBgDQoNCldlJ3JlIG5vdyByZWFkeSB0byBwcm9jZWVkIHRvIHRoZSBmaXZlIG1hY2hpbmUgbGVhcm5pbmcgc3RlcHMhDQoNCiMjIDQuIE1PREVMDQoNClJlY2FsbCBmcm9tIG91ciBmaXJzdCByZWFkaW5nIHRoYXQgdGhlcmUgYXJlIHR3byBnZW5lcmFsIHR5cGVzIG9mDQptb2RlbGluZyBhcHByb2FjaGVzOiB1bnN1cGVydmlzZWQgYW5kIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZy4gSW4NClBhcnQgNCwgd2UgZm9jdXMgb24gc3VwZXJ2aXNlZCBsZWFybmluZyBtb2RlbHMsIHdoaWNoIGFyZSB1c2VkIHRvDQpxdWFudGlmeSByZWxhdGlvbnNoaXBzIGJldHdlZW4gZmVhdHVyZXMgKGUuZy4sIG1vdGl2YXRpb24gYW5kDQpwZXJmb3JtYW5jZSkgYW5kIGEga25vd24gb3V0Y29tZSAoZS5nLiwgc3R1ZGVudCBkcm9wIG91dCkuIFRoZXNlIG1vZGVscw0KY2FuIGJlIHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uIG9mIGJpbmFyeSBvciBjYXRlZ29yaWNhbCBvdXRjb21lcywgYXMNCndlJ2xsIGlsbHVzdHJhdGUgaW4gdGhpcyBzZWN0aW9uLCBvciByZWdyZXNzaW9uIGFzIHdlJ2xsIGRlbW9uc3RyYXRlIGluDQpMZWFybmluZyBMYWIgMi4NCg0KU3BlY2lmaWNhbGx5LCBpbiBQYXJ0IDQgd2Ugd2lsbCBsZWFybiBob3cgdG86DQoNCjEuICAqKlNwbGl0IERhdGEqKiBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0IHRoYXQgd2lsbCBiZSB1c2VkIHRvDQogICAgZGV2ZWxvcCBhIHByZWRpY3RpdmUgbW9kZWw7DQoNCjIuICAqKkNyZWF0ZSBhICJSZWNpcGUiKiogZm9yIG91ciBwcmVkaWN0aXZlIG1vZGVsIGFuZCBsZWFybiBob3cgdG8gZGVhbA0KICAgIHdpdGggbm9taW5hbCBkYXRhIHRoYXQgd2Ugd291bGQgbGlrZSB0byB1c2UgYXMgcHJlZGljdG9yczsNCg0KMy4gICoqU3BlY2lmeSB0aGUgbW9kZWwgYW5kIHdvcmtmbG93KiogYnkgc2VsZWN0aW5nIHRoZSAqZnVuY3Rpb25hbA0KICAgIGZvcm0qIG9mIHRoZSBtb2RlbCB0aGF0IHdlIHdhbnQgYW5kIHVzaW5nIGEgKm1vZGVsIHdvcmtmbG93KiB0byBwYWlyDQogICAgb3VyIG1vZGVsIGFuZCByZWNpcGUgdG9nZXRoZXI7DQoNCjQuICAqKkZpdCBNb2RlbHMqKiB0byBvdXIgdHJhaW5pbmcgc2V0IHVzaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb247DQoNCjUuICAqKkludGVycHJldCBBY2N1cmFjeSoqIG9mIG91ciBtb2RlbCB0byBzZWUgaG93IHdlbGwgb3VyIG1vZGVsIGNhbg0KICAgICJwcmVkaWN0IiBvdXIgb3V0Y29tZSBvZiBpbnRlcmVzdC4NCg0KIyMjIFN0ZXAgMS4gU3BsaXQgZGF0YQ0KDQpUaGUgYXV0aG9ycyBvZiBEYXRhIFNjaWVuY2UgaW4gRWR1Y2F0aW9uIFVzaW5nIFIgKEVzdHJlbGxhZG8gZXQNCmFsLiwyMDIwKSByZW1pbmQgdXMgdGhhdDoNCg0KPiBBdCBpdHMgY29yZSwgbWFjaGluZSBsZWFybmluZyBpcyB0aGUgcHJvY2VzcyBvZiAic2hvd2luZyIgeW91cg0KPiBzdGF0aXN0aWNhbCBtb2RlbCBvbmx5IHNvbWUgb2YgdGhlIGRhdGEgYXQgb25jZSBhbmQgdHJhaW5pbmcgdGhlIG1vZGVsDQo+IHRvIHByZWRpY3QgYWNjdXJhdGVseSBvbiB0aGF0IHRyYWluaW5nIGRhdGFzZXQgKHRoaXMgaXMgdGhlICJsZWFybmluZyINCj4gcGFydCBvZiBtYWNoaW5lIGxlYXJuaW5nKS4gVGhlbiwgdGhlIG1vZGVsIGFzIGRldmVsb3BlZCBvbiB0aGUNCj4gdHJhaW5pbmcgZGF0YSBpcyBzaG93biBuZXcgZGF0YSAtIGRhdGEgeW91IGhhZCBhbGwgYWxvbmcsIGJ1dCBoaWQgZnJvbQ0KPiB5b3VyIGNvbXB1dGVyIGluaXRpYWxseSAtIGFuZCB5b3Ugc2VlIGhvdyB3ZWxsIHRoZSBtb2RlbCB0aGF0IHlvdQ0KPiBkZXZlbG9wZWQgb24gdGhlIHRyYWluaW5nIGRhdGEgcGVyZm9ybXMgb24gdGhpcyBuZXcgdGVzdGluZyBkYXRhLg0KPiBFdmVudHVhbGx5LCB5b3UgbWlnaHQgdXNlIHRoZSBtb2RlbCBvbiBlbnRpcmVseSBuZXcgZGF0YS4NCg0KIyMjIyBUcmFpbmluZyBhbmQgVGVzdGluZyBTZXRzDQoNCkl0IGlzIHRoZXJlZm9yZSBjb21tb24gd2hlbiBiZWdpbm5pbmcgYSBtb2RlbGluZyBwcm9qZWN0IHRvIFtzZXBhcmF0ZQ0KdGhlIGRhdGEgc2V0XShodHRwczovL2Jvb2tkb3duLm9yZy9tYXgvRkVTL2RhdGEtc3BsaXR0aW5nLmh0bWwpIGludG8gdHdvDQpwYXJ0aXRpb25zOg0KDQotICAgVGhlICp0cmFpbmluZyBzZXQqIGlzIHVzZWQgdG8gZXN0aW1hdGUsIGRldmVsb3AgYW5kIGNvbXBhcmUgbW9kZWxzOw0KICAgIGZlYXR1cmUgZW5naW5lZXJpbmcgdGVjaG5pcXVlczsgdHVuZSBtb2RlbHMsIGV0Yy4NCg0KLSAgIFRoZSAqdGVzdCBzZXQqIGlzIGhlbGQgaW4gcmVzZXJ2ZSB1bnRpbCB0aGUgZW5kIG9mIHRoZSBwcm9qZWN0LCBhdA0KICAgIHdoaWNoIHBvaW50IHRoZXJlIHNob3VsZCBvbmx5IGJlIG9uZSBvciB0d28gbW9kZWxzIHVuZGVyIHNlcmlvdXMNCiAgICBjb25zaWRlcmF0aW9uLiBJdCBpcyB1c2VkIGFzIGFuIHVuYmlhc2VkIHNvdXJjZSBmb3IgbWVhc3VyaW5nIGZpbmFsDQogICAgbW9kZWwgcGVyZm9ybWFuY2UuDQoNClRoZXJlIGFyZSBkaWZmZXJlbnQgd2F5cyB0byBjcmVhdGUgdGhlc2UgcGFydGl0aW9ucyBvZiB0aGUgZGF0YSBhbmQNCnRoZXJlIGlzIG5vIHVuaWZvcm0gZ3VpZGVsaW5lIGZvciBkZXRlcm1pbmluZyBob3cgbXVjaCBkYXRhIHNob3VsZCBiZQ0Kc2V0IGFzaWRlIGZvciB0ZXN0aW5nLiBUaGUgcHJvcG9ydGlvbiBvZiBkYXRhIGNhbiBiZSBkcml2ZW4gYnkgbWFueQ0KZmFjdG9ycywgaW5jbHVkaW5nIHRoZSBzaXplIG9mIHRoZSBvcmlnaW5hbCBwb29sIG9mIHNhbXBsZXMgYW5kIHRoZQ0KdG90YWwgbnVtYmVyIG9mIHByZWRpY3RvcnMuwqANCg0KQWZ0ZXIgeW91IGRlY2lkZSBob3cgbXVjaCB0byBzZXQgYXNpZGUsIHRoZSBtb3N0IGNvbW1vbiBhcHByb2FjaCBmb3INCmFjdHVhbGx5IHBhcnRpdGlvbmluZyB5b3VyIGRhdGEgaXMgdG8gdXNlIGEgcmFuZG9tIHNhbXBsZS4gRm9yIG91cg0KcHVycG9zZXMsIHdlJ2xsIHVzZSByYW5kb20gc2FtcGxpbmcgdG8gc2VsZWN0IDIwJSBmb3IgdGhlIHRlc3Qgc2V0IGFuZA0KdXNlIHRoZSByZW1haW5kZXIgZm9yIHRoZSB0cmFpbmluZyBzZXQsIHdoaWNoIGFyZSB0aGUgZGVmYXVsdHMgZm9yIHRoZQ0Ke1tyc2FtcGxlXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JzYW1wbGUvKX0gcGFja2FnZS4NCg0KIyMjIyBTcGxpdCBEYXRhIFNldHMNCg0KVG8gc3BsaXQgb3VyIGRhdGEsIHdlIHdpbGwgYmUgdXNpbmcgb3VyIGZpcnN0IHt0aWR5bW9kZWxzfSBmdW5jdGlvbiAtDQpgaW5pdGlhbF9zcGxpdCgpYC4NCg0KVGhlIGZ1bmN0aW9uIGBpbml0aWFsX3NwbGl0KClgIGZ1bmN0aW9uIGZyb20gdGhlIHtyc2FtcGxlfSBwYWNrYWdlIHRha2VzDQp0aGUgb3JpZ2luYWwgZGF0YSBhbmQgc2F2ZXMgdGhlIGluZm9ybWF0aW9uIG9uIGhvdyB0byBtYWtlIHRoZQ0KcGFydGl0aW9ucy4gVGhlIHtyc2FtcGxlfSBwYWNrYWdlIGhhcyB0d28gYXB0bHkgbmFtZWQgZnVuY3Rpb25zIGZvcg0KY3JlYXRlZCBhIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEgc2V0IGNhbGxlZCBgdHJhaW5pbmcoKWAgYW5kDQpgdGVzdGluZygpYCwgcmVzcGVjdGl2ZWx5Lg0KDQpSdW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIHNwbGl0IHRoZSBkYXRhOg0KDQpgYGB7cn0NCg0KYGBgDQoNCioqTm90ZSoqOiBTaW5jZSByYW5kb20gc2FtcGxpbmcgdXNlcyByYW5kb20gbnVtYmVycywgaXQgaXMgaW1wb3J0YW50IHRvDQpzZXQgdGhlIHJhbmRvbSBudW1iZXIgc2VlZCB1c2luZyB0aGUgYHNldC5zZWVkKClgIGZ1bmN0aW9uLiBUaGlzIGVuc3VyZXMNCnRoYXQgdGhlIHJhbmRvbSBudW1iZXJzIGNhbiBiZSByZXByb2R1Y2VkIGF0IGEgbGF0ZXIgdGltZSAoaWYgbmVlZGVkKS4NCldlIHBpY2sgdGhlIGZpcnN0IGRhdGUgb24gd2hpY2ggd2UgbWF5IGNhcnJ5IG91dCB0aGlzIGxlYXJuaW5nIGxhYiBhcw0KdGhlIHNlZWQsIGJ1dCBhbnkgbnVtYmVyIHdpbGwgd29yayENCg0KIyMjIyAqKvCfkYkgWW91ciBUdXJuKiogKiripLUqKg0KDQpHbyBhaGVhZCBhbmQgdHlwZSBgZGF0YV90cmFpbmAgYW5kIGBkYXRhX3Rlc3RgIGludG8gdGhlIGNvbnNvbGUgKGluDQpzdGVwcykgdG8gY2hlY2sgdGhhdCB0aGlzIGRhdGEgc2V0IGluZGVlZCBoYXMgODAlIG9mIHRoZSBudW1iZXIgb2YNCm9ic2VydmF0aW9ucyBhcyBpbiB0aGUgbGFyZ2VyIGRhdGEuIERvIHRoYXQgaW4gdGhlIGNodW5rIGJlbG93Og0KDQpgYGB7cn0NCg0KDQpgYGANCg0KIyMjIFN0ZXAgMjogQ3JlYXRlIGEgIlJlY2lwZSINCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBpbnRyb2R1Y2UgYW5vdGhlciB0aWR5bW9kZWxzIHBhY2thZ2UgbmFtZWQNCntbcmVjaXBlc10oaHR0cHM6Ly9yZWNpcGVzLnRpZHltb2RlbHMub3JnLyl9LCB3aGljaCBpcyBkZXNpZ25lZCB0byBoZWxwDQp5b3UgcHJlcGFyZSB5b3VyIGRhdGEgKmJlZm9yZSogdHJhaW5pbmcgeW91ciBtb2RlbC4gUmVjaXBlcyBhcmUgYnVpbHQgYXMNCmEgc2VyaWVzIG9mIHByZXByb2Nlc3Npbmcgc3RlcHMsIHN1Y2ggYXM6DQoNCi0gICBjb252ZXJ0aW5nIHF1YWxpdGF0aXZlIHByZWRpY3RvcnMgdG8gaW5kaWNhdG9yIHZhcmlhYmxlcyAoYWxzbyBrbm93bg0KICAgIGFzIGR1bW15IHZhcmlhYmxlcyksDQoNCi0gICB0cmFuc2Zvcm1pbmcgZGF0YSB0byBiZSBvbiBhIGRpZmZlcmVudCBzY2FsZSAoZS5nLiwgdGFraW5nIHRoZQ0KICAgIGxvZ2FyaXRobSBvZiBhIHZhcmlhYmxlKSwNCg0KLSAgIHRyYW5zZm9ybWluZyB3aG9sZSBncm91cHMgb2YgcHJlZGljdG9ycyB0b2dldGhlciwNCg0KLSAgIGV4dHJhY3Rpbmcga2V5IGZlYXR1cmVzIGZyb20gcmF3IHZhcmlhYmxlcyAoZS5nLiwgZ2V0dGluZyB0aGUgZGF5IG9mDQogICAgdGhlIHdlZWsgb3V0IG9mIGEgZGF0ZSB2YXJpYWJsZSksIGFuZCBzbyBvbi4NCg0KSWYgeW91IGFyZSBmYW1pbGlhciB3aXRoIFIncyBmb3JtdWxhIGludGVyZmFjZSwgYSBsb3Qgb2YgdGhpcyBtaWdodA0Kc291bmQgZmFtaWxpYXIgYW5kIGxpa2Ugd2hhdCBhIGZvcm11bGEgYWxyZWFkeSBkb2VzLiBSZWNpcGVzIGNhbiBiZSB1c2VkDQp0byBkbyBtYW55IG9mIHRoZSBzYW1lIHRoaW5ncywgYnV0IHRoZXkgaGF2ZSBhIG11Y2ggd2lkZXIgcmFuZ2Ugb2YNCnBvc3NpYmlsaXRpZXMuDQoNCiMjIyMgKipBZGQgYSBmb3JtdWxhKioNCg0KVG8gZ2V0IHN0YXJ0ZWQsIGxldCdzIGNyZWF0ZSBhIHJlY2lwZSBmb3IgYSBzaW1wbGUgbG9naXN0aWMgcmVncmVzc2lvbg0KbW9kZWwuIEJlZm9yZSB0cmFpbmluZyB0aGUgbW9kZWwsIHdlIGNhbiB1c2UgYSByZWNpcGUuDQoNClRoZQ0KW2ByZWNpcGUoKWBmdW5jdGlvbl0oaHR0cHM6Ly9yZWNpcGVzLnRpZHltb2RlbHMub3JnL3JlZmVyZW5jZS9yZWNpcGUuaHRtbCkNCmFzIHdlIHVzZWQgaXQgaGVyZSBoYXMgdHdvIGFyZ3VtZW50czoNCg0KLSAgIEEgKipmb3JtdWxhKiouIEFueSB2YXJpYWJsZSBvbiB0aGUgbGVmdC1oYW5kIHNpZGUgb2YgdGhlIHRpbGRlIChgfmApDQogICAgaXMgY29uc2lkZXJlZCB0aGUgbW9kZWwgb3V0Y29tZSAoYGNvZGVgLCBpbiBvdXIgcHJlc2VudCBjYXNlKS4gT24NCiAgICB0aGUgcmlnaHQtaGFuZCBzaWRlIG9mIHRoZSB0aWxkZSBhcmUgdGhlIHByZWRpY3RvcnMuIFZhcmlhYmxlcyBtYXkNCiAgICBiZSBsaXN0ZWQgYnkgbmFtZSwgb3IgeW91IGNhbiB1c2UgdGhlIGRvdCAoYC5gKSB0byBpbmRpY2F0ZSBhbGwNCiAgICBvdGhlciB2YXJpYWJsZXMgYXMgcHJlZGljdG9ycy4NCg0KLSAgIFRoZSAqKmRhdGEqKi4gQSByZWNpcGUgaXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBkYXRhIHNldCB1c2VkIHRvDQogICAgY3JlYXRlIHRoZSBtb2RlbC4gVGhpcyB3aWxsIHR5cGljYWxseSBiZSB0aGUgKnRyYWluaW5nKiBzZXQsIHNvDQogICAgYGRhdGEgPSB0cmFpbl9kYXRhYCBoZXJlLiBOYW1pbmcgYSBkYXRhIHNldCBkb2Vzbid0IGFjdHVhbGx5IGNoYW5nZQ0KICAgIHRoZSBkYXRhIGl0c2VsZjsgaXQgaXMgb25seSB1c2VkIHRvIGNhdGFsb2cgdGhlIG5hbWVzIG9mIHRoZQ0KICAgIHZhcmlhYmxlcyBhbmQgdGhlaXIgdHlwZXMsIGxpa2UgZmFjdG9ycywgaW50ZWdlcnMsIGRhdGVzLCBldGMuDQoNCkxldCdzIGNyZWF0ZSBhIHJlY2lwZSB3aGVyZSB3ZSBwcmVkaWN0IGBwYXNzYCAodGhlIG91dGNvbWUgdmFyaWFibGUpIG9uDQp0aGUgYmFzaXMgb2YgdGhlIGBkaXNhYmlsaXR5YCBhbmQgYGltZF9iYW5kYCAocHJlZGljdG9yKSB2YXJpYWJsZXMuDQoNCg0KDQojIyMgU3RlcCAzOiBTcGVjaWZ5IHRoZSBtb2RlbCBhbmQgd29ya2Zsb3cNCg0KV2l0aCB0aWR5bW9kZWxzLCB3ZSBzdGFydCBidWlsZGluZyBhIG1vZGVsIGJ5IHNwZWNpZnlpbmcgdGhlICpmdW5jdGlvbmFsDQpmb3JtKiBvZiB0aGUgbW9kZWwgdGhhdCB3ZSB3YW50IHVzaW5nIHRoZSBbe3BhcnNuaXB9DQpwYWNrYWdlXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3BhcnNuaXAvKS4gU2luY2Ugb3VyIG91dGNvbWUgaXMNCmJpbmFyeSwgdGhlIG1vZGVsIHR5cGUgd2Ugd2lsbCB1c2UgaXPCoCJbbG9naXN0aWMNCnJlZ3Jlc3Npb25dKGh0dHBzOi8vcGFyc25pcC50aWR5bW9kZWxzLm9yZy9yZWZlcmVuY2UvbG9naXN0aWNfcmVnLmh0bWwpLiINCldlIGNhbiBkZWNsYXJlIHRoaXMgd2l0aCBgbG9naXN0aWNfcmVnKClgIGFuZCBhc3NpZ24gdG8gYW4gb2JqZWN0IHdlDQp3aWxsIGxhdGVyIHVzZSBpbiBvdXIgd29ya2Zsb3c6DQoNClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gZmluaXNoIHNwZWNpZnlpbmcgb3VyIG1vZGVsOg0KDQoNClRoYXQgaXMgcHJldHR5IHVuZGVyd2hlbG1pbmcgc2luY2UsIG9uIGl0cyBvd24sIGl0IGRvZXNuJ3QgcmVhbGx5IGRvDQptdWNoLiBIb3dldmVyLCBub3cgdGhhdCB0aGUgdHlwZSBvZiBtb2RlbCBoYXMgYmVlbiBzcGVjaWZpZWQsIGEgbWV0aG9kDQpmb3LCoCpmaXR0aW5nKsKgb3IgdHJhaW5pbmcgdGhlIG1vZGVsIGNhbiBiZSBzdGF0ZWQgdXNpbmcgdGhlICoqZW5naW5lKiouDQoNCiMjIyMgKipTdGFydCB5b3VyIGVuZ2luZSoqDQoNClRvIHNldCB0aGUgKmVuZ2luZSwqIGxldCdzIHJld3JpdGUgdGhlIGNvZGUgYWJvdmUgYW5kICJwaXBlIiBpbiB0aGUNCmBzZXRfZW5naW5lKCJnbG0iKWAgZnVuY3Rpb24gYW5kIGBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKSlgIHRvIHNldA0KdGhlICIqbW9kZSoiIHRvIGNsYXNzaWZpY2F0aW9uLiBOb3RlIHRoYXQgdGhpcyBjb3VsZCBhbHNvIGJlIGNoYW5nZWQgdG8NCnJlZ3Jlc3Npb24gZm9yIGEgY29udGludW91cy9udW1lcmljIG91dGNvbWUuDQoNClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gZmluaXNoIHNwZWNpZnlpbmcgb3VyIG1vZGVsOg0KDQoNClRoZSBlbmdpbmUgdmFsdWUgaXMgb2Z0ZW4gYSBtYXNoLXVwIG9mIGRpZmZlcmVudCBwYWNrYWdlcyB0aGF0IGNhbiBiZQ0KdXNlZCB0byBmaXQgb3IgdHJhaW4gdGhlIG1vZGVsIGFzIHdlbGwgYXMgdGhlIGVzdGltYXRpb24gbWV0aG9kLiBGb3INCmV4YW1wbGUsIHdlIHdpbGwgdXNlICJnbG0iIGEgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsIGZvciBiaW5hcnkNCm91dGNvbWVzIGFuZCBkZWZhdWx0IGZvciBsb2dpc3RpYyByZWdyZXNzaW9uIGluIHRoZSB7cGFyc25pcH0gcGFja2FnZS4NCg0KIyMjIyAqKkFkZCB0byB3b3JrZmxvdyoqDQoNCk5vdyB3ZSBjYW4gdXNlIHRoZSByZWNpcGUgY3JlYXRlZCBlYXJsaWVyIGFjcm9zcyBzZXZlcmFsIHN0ZXBzIGFzIHdlDQp0cmFpbiBhbmQgdGVzdCBvdXIgbW9kZWwuIFRvIHNpbXBsaWZ5IHRoaXMgcHJvY2Vzcywgd2UgY2FuIHVzZSBhICptb2RlbA0Kd29ya2Zsb3cqLCB3aGljaCBwYWlycyBhIG1vZGVsIGFuZCByZWNpcGUgdG9nZXRoZXIuDQoNClRoaXMgaXMgYSBzdHJhaWdodGZvcndhcmQgYXBwcm9hY2ggYmVjYXVzZSBkaWZmZXJlbnQgcmVjaXBlcyBhcmUgb2Z0ZW4NCm5lZWRlZCBmb3IgZGlmZmVyZW50IG1vZGVscywgc28gd2hlbiBhIG1vZGVsIGFuZCByZWNpcGUgYXJlIGJ1bmRsZWQsIGl0DQpiZWNvbWVzIGVhc2llciB0byB0cmFpbiBhbmQgdGVzdMKgKndvcmtmbG93cyouDQoNCldlJ2xsIHVzZSB0aGV7W3dvcmtmbG93c10oaHR0cHM6Ly93b3JrZmxvd3MudGlkeW1vZGVscy5vcmcvKX0gcGFja2FnZQ0KZnJvbSB0aWR5bW9kZWxzIHRvIGJ1bmRsZSBvdXIgcGFyc25pcCBtb2RlbCAoYGxyX21vZGApIHdpdGggb3VyIGZpcnN0DQpyZWNpcGUgKGBscl9yZWNpcGVfMWApLg0KDQoNCiMjIyBTdGVwIDQ6IEZpdCBtb2RlbA0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgc2luZ2xlIHdvcmtmbG93IHRoYXQgY2FuIGJlIHVzZWQgdG8gcHJlcGFyZSB0aGUNCnJlY2lwZSBhbmQgdHJhaW4gdGhlIG1vZGVsIGZyb20gdGhlIHJlc3VsdGluZyBwcmVkaWN0b3JzLCB3ZSBjYW4gdXNlIHRoZQ0KYGZpdCgpYCBmdW5jdGlvbiB0byBmaXQgb3VyIG1vZGVsIHRvIG91ciBgdHJhaW5fZGF0YWAuIEFuZCBhZ2Fpbiwgd2Ugc2V0DQphIHJhbmRvbSBudW1iZXIgc2VlZCB0byBlbnN1cmUgdGhhdCBpZiB3ZSBydW4gdGhpcyBzYW1lIGNvZGUgYWdhaW4sIHdlDQp3aWxsIGdldCB0aGUgc2FtZSByZXN1bHRzIGluIHRlcm1zIG9mIHRoZSBkYXRhIHBhcnRpdGlvbjoNCg0KRmluYWxseSwgd2UnbGwgZml0IG91ciBtb2RlbC4NCg0KDQojIyMjICoq8J+RiSBZb3VyIFR1cm4qKiAqKuKktSoqDQoNCkltcG9ydGFudGx5LCBoZXJlLCB3ZSBjYW4gbG9vayBhdCB0aGUgbW9kZWwuIFR5cGUgYGZpdHRlZF9tb2RlbGAgaW4gYQ0KY29kZSBjaHVuayB0byB0YWtlIGEgbG9vay4NCg0KDQpOb3RlIHRoYXQgd2hpbGUgd2UgZG9uJ3QgdHlwaWNhbGx5IGludGVycHJldCB0aGUgY29lZmZpY2llbnRzIGZvciBhDQptYWNoaW5lIGxlYXJuaW5nIG1vZGVsLCBpdCdzIGltcG9ydGFudCB0byByZWNvZ25pemUgdGhhdCBtYW55IG1vZGVscw0KKmRvKiBwcm9kdWNlIGNvZWZmaWNpZW50cyB3ZSAqY291bGQqIGludGVycHJldC4gSW5zdGVhZCwgd2UgZm9jdXMgb24gaG93DQp0aGUgbW9kZWwgZG9lcyB3aXRoIHJlc3BlY3QgdG8gcHJlZGljdGluZyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLg0KDQoqKk9ic2VydmF0aW9ucyoqDQoNCldyaXRlIGRvd24gYSBicmllZiBvYnNlcnZhdGlvbiBvZiB0aGUgb3V0cHV0IGFib3ZlLCBmb2N1c2luZyBvbiB0aGUNCmNvZWZmaWNpZW50czoNCg0KLSAgIGRpc2FiaWxpdHkgaGFzIGEgbmVnYXRpdmUgcmVsYXRpb25zaGlwIHdpdGggcGFzcyBhbmQgaW1kX2JhbmQgaGFzIGENCiAgICBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgd2l0aCBwYXNzLg0KDQojIyMjIFRoZSBsYXN0X2ZpdCBmdW5jdGlvbg0KDQpGaW5hbGx5LCB3ZSdsbCB1c2UgdGhlIGBsYXN0X2ZpdGAgZnVuY3Rpb24sIHdoaWNoIGlzIHRoZSBrZXkgaGVyZTogbm90ZQ0KdGhhdCBpdCB1c2VzIHRoZSBgdHJhaW5fdGVzdF9zcGxpdGAgZGF0YS0tLW5vdCBqdXN0IHRoZSB0cmFpbmluZyBkYXRhLg0KDQpIZXJlLCB0aGVuLCB3ZSBmaXQgdGhlIGRhdGEgKnVzaW5nIHRoZSB0cmFpbmluZyBkYXRhIHNldCogYW5kIGV2YWx1YXRlDQppdHMgYWNjdXJhY3kgdXNpbmcgdGhlICp0ZXN0aW5nIGRhdGEgc2V0KiAod2hpY2ggaXMgbm90IHVzZWQgdG8gdHJhaW4NCnRoZSBtb2RlbCkuDQoNCmBgYHtyfQ0KDQpgYGANCg0KVHlwZSBgZmluYWxfZml0YCBiZWxvdzsgdGhpcyBpcyB0aGUgZmluYWwsIGZpdHRlZCBtb2RlbC0tLW9uZSB0aGF0IGNhbg0KYmUgaW50ZXJwcmV0ZWQgZnVydGhlciBpbiB0aGUgbmV4dCBzdGVwIQ0KDQpgYGB7cn0NCg0KYGBgDQoNCllvdSBtYXkgc2VlIGEgbWVzc2FnZS93YXJuaW5nIGFib3ZlIG9yIHdoZW4geW91IGV4YW1pbmUgYGZpbmFsX2ZpdGA7IHlvdQ0KY2FuIHNhZmVseSBpZ25vcmUgdGhhdC4NCg0KIyMjIFN0ZXAgNTogSW50ZXJwcmV0IGFjY3VyYWN5DQoNClJ1biB0aGUgY29kZSBiZWxvdyB0byBleGFtaW5lIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlICp0ZXN0KiBzcGxpdCBvZg0KZGF0YS4gTm90ZSB0aGF0IHRoZSByb3cgSUQgaXMgaW4gdGhlIG91dHB1dCBiZWxvdywgYnV0IHRoaXMgZG9lc24ndA0KY29ycmVzcG9uZCBvbmUtb25lIHRvIHRoZSBJRCB2YXJpYWJsZXMgdXNlZCBpbiB0aGUgcHJlc2VudGF0aW9uL1NoaW55Lg0KDQoNCg0KVGhpcyBpcyBvdXIgZmlyc3Qgc2V0IG9mIHJlYWwgb3V0cHV0ISBOb3RlIHR3byB0aGluZ3M6DQoNCjEuICBgLnByZXNfY2xhc3NgOiBUaGlzIGlzIHRoZSAqcHJlZGljdGVkKiBjb2RlXA0KMi4gIGBwYXNzYDogVGhpcyBpcyB0aGUga25vd24gKmNvZGUqDQoNCldoZW4gdGhlc2UgYXJlICoqdGhlIHNhbWUqKiwgdGhlIG1vZGVsIHByZWRpY3RlZCB0aGUgY29kZSAqY29ycmVjdGx5KjsNCndoZW4gdGhleSBhcmVuJ3QgdGhlIHNhbWUsIHRoZSBtb2RlbCB3YXMgaW5jb3JyZWN0Lg0KDQpJbXBvcnRhbnRseSwgd2UgY2FuICpzdW1tYXJpemUqIGFjcm9zcyBhbGwgb2YgdGhlc2UgY29kZXMuIE9uZSB3YXkgdG8gZG8NCnRoaXMgaXMgc3RyYWlnaHRmb3J3YXJkOyBob3cgbWFueSBvZiB0aGUgY29kZXMgd2VyZSB0aGUgc2FtZSwgYXMgaW4gdGhlDQpmb2xsb3dpbmcgY2h1bmsgb2YgY29kZToNCg0KDQoNCllvdSBtYXkgbm90aWNlIHNvbWUgb2YgdGhlIHJvd3MgbWF5IGJlIG1pc3NpbmcgdmFsdWVzLiBUaGlzIGlzIGJlY2F1c2UNCnRoZXJlIHdlcmUgc29tZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgYGltZF9iYW5kYCB2YXJpYWJsZSwgYW5kIGZvciB0aGlzDQptYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobSAodGhlIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCksIG1pc3NpbmcNCnZhbHVlcyByZXN1bHQgaW4gcm93LXdpc2UgZGVsZXRpb24uDQoNClRoYXQncyBoZWxwZnVsLCBidXQgdGhlcmUncyBvbmUgbW9yZSBzdGVwIHdlIGNhbiB0YWtlIC0tIGNvdW50aW5nIHVwIHRoZQ0KdmFsdWVzIG9mIGBjb3JyZWN0YDoNCg0KDQoNCiMjIyMgKirwn5GJIFlvdXIgVHVybioqICoq4qS1KioNCg0KTGV0J3MgaW50ZXJwcmV0IHRoZSBhYm92ZS4gSWYgdGhlIHZhbHVlIG9mIGBjb3JyZWN0YCBpcyBgVFJVRWAgd2hlbiB0aGUNCnByZWRpY3RlZCBhbmQga25vd24gY29kZSBhcmUgdGhlIHNhbWUsIHdoYXQgZG9lcyB0aGUgYHBlcmNlbnRgIGNvbHVtbg0KdGVsbCB1cz8gQWRkIG9uZSBvciBtb3JlIG5vdGVzIHRvIHRoZSBkYXNoZXMgYmVsb3c6DQoNCi0gICBJIGNhbm5vdCBzZWVtIHRvIGdldCBhbnkgZnVuY3Rpb25zIHRvIHdvcmsgaW4gcmVsYXRpb24gdG8gdGhpcyBvciBtYW55IG9mIHRoZSBwcmV2aW91cyBjb25jZXB0cy4gSSB3b3VsZCBndWVzcyB0aGF0ICdwZXJjZW50JyBpcyBpbiByZWZlcmVuY2UgdG8gdGhlIGFtb3VudCBvZiB0cnVlIHZhbHVlcy4NCg0KQSBzaG9ydC1jdXQgdG8gdGhlIGFib3ZlIGlzIGJlbG93OyAqKndlJ2xsIHVzZSB0aGlzIHNob3J0LWN1dCBmcm9tIGhlcmUNCmZvcndhcmQqKiwgaGF2aW5nIHNlZW4gaG93IGFjY3VyYWN5IGlzIGNhbGN1bGF0ZWQuDQoNCmBgYHtyfQ0KDQpgYGANCg0KSG93IGFjY3VyYXRlIHdhcyBvdXIgcHJlZGljdGl2ZSBtb2RlbD8gQ29uc2lkZXIgaG93IHdlbGwgb3VyIG1vZGVsIHdvdWxkDQpoYXZlIGRvbmUgYnkgY2hhbmNlIGFsb25lIC0tIHdoYXQgd291bGQgdGhlIGFjY3VyYWN5IGJlIGluIHRoYXQgY2FzZQ0KKHdpdGggdGhlIG1vZGVsIHByZWRpY3RpbmcgcGFzcyBvbmUtaGFsZiBvZiB0aGUgdGltZSk/DQoNCkN1cmlvdXNseSwgcmFuZG9tbHkgcGlja2luZyBhIDAgKGRpZCBub3QgcGFzcykgb3IgYSAxIChwYXNzZWQpIHdpbGwNCiphbHdheXMqIGxlYWQgdG8gYXJvdW5kIGEgNTAlIGFjY3VyYWN5LCByZWdhcmRsZXNzIG9mIGhvdyBtYW55DQpvYnNlcnZhdGlvbnMgYXJlIGFjdHVhbGx5IGFzc29jaWF0ZWQgd2l0aCBhIDAgb3IgYSAxLg0KDQoqKk9ic2VydmF0aW9uKioNCg0KTGV0J3Mgc3RlcCBiYWNrIGEgYml0LiBIb3cgd2VsbCAqY291bGQqIHdlIGRvIGlmIHdlIGluY2x1ZGUgbW9yZSBkYXRhPw0KQW5kIGhvdyAqdXNlZnVsKiBjb3VsZCBzdWNoIGEgbW9kZWwgYmUgaW4gdGhlIHJlYWwgd29ybGQ/IFdlJ2xsIGRpdmUNCmludG8gdGhlc2UgcXVlc3Rpb25zIG1vcmUgb3ZlciB0aGUgZm9ydGhjb21pbmcgbGVhcm5pbmcgbGFicy4NCg0KVGhhdCdzIGl0IGZvciBub3c7IHRoZSBjb3JlIHBhcnRzIG9mIG1hY2hpbmUgbGVhcm5pbmcgYXJlIHVzZWQgaW4gdGhlDQphYm92ZSBzdGVwcyB5b3UgdG9vazsgd2hhdCB3ZSdsbCBkbyBhZnRlciB0aGlzIGxlYW5pbmcgbGFiIG9ubHkgYWRkcw0KbnVhbmNlIGFuZCBjb21wbGV4aXR5IHRvIHdoYXQgd2UndmUgYWxyZWFkeSBkb25lLg0KDQojIyA1LiBDT01NVU5JQ0FURQ0KDQpUaGUgZmluYWwgc3RlcCBpbiB0aGUgd29ya2Zsb3cvcHJvY2VzcyBpcyBzaGFyaW5nIHRoZSByZXN1bHRzIG9mIHlvdXINCmFuYWx5c2lzIHdpdGggd2lkZXIgYXVkaWVuY2UuIEtydW1tIGV0IGFsLiAoMjAxOCkgaGF2ZSBvdXRsaW5lZCB0aGUNCmZvbGxvd2luZyAzLXN0ZXAgcHJvY2VzcyBmb3IgY29tbXVuaWNhdGluZyB3aXRoIGVkdWNhdGlvbiBzdGFrZWhvbGRlcnMNCmZpbmRpbmdzIGZyb20gYW4gYW5hbHlzaXM6DQoNCjEuICAqKlNlbGVjdC4qKiBDb21tdW5pY2F0aW5nIHdoYXQgb25lIGhhcyBsZWFybmVkIGludm9sdmVzIHNlbGVjdGluZw0KICAgIGFtb25nIHRob3NlIGFuYWx5c2VzIHRoYXQgYXJlIG1vc3QgaW1wb3J0YW50IGFuZCBtb3N0IHVzZWZ1bCB0byBhbg0KICAgIGludGVuZGVkIGF1ZGllbmNlLCBhcyB3ZWxsIGFzIHNlbGVjdGluZyBhIGZvcm0gZm9yIGRpc3BsYXlpbmcgdGhhdA0KICAgIGluZm9ybWF0aW9uLCBzdWNoIGFzIGEgZ3JhcGggb3IgdGFibGUgaW4gc3RhdGljIG9yIGludGVyYWN0aXZlIGZvcm0sDQogICAgaS5lLsKgYSAiZGF0YSBwcm9kdWN0LiINCg0KMi4gICoqUG9saXNoKiouIEFmdGVyIGNyZWF0aW5nIGluaXRpYWwgdmVyc2lvbnMgb2YgZGF0YSBwcm9kdWN0cywNCiAgICByZXNlYXJjaCB0ZWFtcyBvZnRlbiBzcGVuZCB0aW1lIHJlZmluaW5nIG9yIHBvbGlzaGluZyB0aGVtLCBieQ0KICAgIGFkZGluZyBvciBlZGl0aW5nIHRpdGxlcywgbGFiZWxzLCBhbmQgbm90YXRpb25zIGFuZCBieSB3b3JraW5nIHdpdGgNCiAgICBjb2xvcnMgYW5kIHNoYXBlcyB0byBoaWdobGlnaHQga2V5IHBvaW50cy4NCg0KMy4gICoqTmFycmF0ZS4qKiBXcml0aW5nIGEgbmFycmF0aXZlIHRvIGFjY29tcGFueSB0aGUgZGF0YSBwcm9kdWN0cw0KICAgIGludm9sdmVzLCBhdCBhIG1pbmltdW0sIHBhaXJpbmcgYSBkYXRhIHByb2R1Y3Qgd2l0aCBpdHMgcmVsYXRlZA0KICAgIHJlc2VhcmNoIHF1ZXN0aW9uLCBkZXNjcmliaW5nIGhvdyBiZXN0IHRvIGludGVycHJldCB0aGUgZGF0YQ0KICAgIHByb2R1Y3QsIGFuZCBleHBsYWluaW5nIHRoZSB3YXlzIGluIHdoaWNoIHRoZSBkYXRhIHByb2R1Y3QgaGVscHMNCiAgICBhbnN3ZXIgdGhlIHJlc2VhcmNoIHF1ZXN0aW9uLg0KDQpGb3IgeW91ciBsZWFybmluZyBNTCBMZWFybmluZyBMYWIgMSBCYWRnZSwgeW91IHdpbGwgaGF2ZSBhbiBvcHBvcnR1bml0eQ0KdG8gY3JlYXRlIGEgc2ltcGxlICJkYXRhIHByb2R1Y3QiIGRlc2lnbmVkIHRvIGlsbHVzdHJhdGUgaW5zaWdodHMgc29tZQ0KaW5zaWdodHMgZ2FpbmVkIGZyb20geW91ciBtb2RlbCBhbmQgaWRlYWxseSBoaWdobGlnaHQgYW4gImFjdGlvbiBzdGVwIg0KdGhhdCBjYW4gYmUgdGFrZW4gdG8gYWN0IHVwb24geW91ciBmaW5kaW5ncy4NCg0KIyMjIyAqKvCfkYkgWW91ciBUdXJuKiogKiripLUqKg0KDQpGb3Igbm93LCB3ZSB3aWxsIHdyYXAgdXAgdGhpcyBjYXNlIHN0dWR5IGJ5IGNvbnZlcnRpbmcgb3VyIHdvcmsgaW50byBhDQp3ZWJwYWdlIHRoYXQgY2FuIGJlIHVzZWQgdG8gY29tbXVuaWNhdGUgeW91ciBsZWFybmluZyBhbmQgZGVtb25zdHJhdGUNCnNvbWUgb2YgeW91ciBuZXcgUiBza2lsbHMuIFRvIGRvIHNvLCB5b3Ugd2lsbCBuZWVkIHRvICJrbml0IiB5b3VyDQpkb2N1bWVudCBieSBjbGlja2luZyB0aGUgYnV0dG9uIGluIHRoZSBtZW51IGJhciBhdCB0aGUgdGhlIHRvcCBvZiB0aGlzDQpmaWxlLiBUaGlzIHdpbGwgZG8gdHdvIHRoaW5nczsgaXQgd2lsbDoNCg0KMS4gIGNoZWNrIHRocm91Z2ggYWxsIHlvdXIgY29kZSBmb3IgYW55IGVycm9yczsgYW5kLA0KDQoyLiAgY3JlYXRlIGEgZmlsZSBpbiB5b3VyIGRpcmVjdG9yeSB0aGF0IHlvdSBjYW4gdXNlIHRvIHNoYXJlIHlvdSB3b3JrDQogICAgdGhyb3VnaCBbUG9zaXQNCiAgICBDbG91ZF0oaHR0cHM6Ly9wb3NpdC5jbG91ZC9sZWFybi9ndWlkZSNwdWJsaXNoLWZyb20tY2xvdWQpIChzZWUNCiAgICBzY3JlZW5zaG90IGV4YW1wbGUgYmVsb3cgdG8gcHVibGlzaCksIFtSUHVic10oIzApICwgW0dpdEh1Yg0KICAgIFBhZ2VzXSgjMCksIFtRdWFydG8gUHViXSgjMCksIG9yIGFueSBbb3RoZXIgbWV0aG9kc10oIzApLg0KDQoqKk5vdGU6KiogQmVmb3JlIGtuaXR0aW5nLCBtYWtlIHN1cmUgeW91IGNoYW5nZSB0aGUgYGF1dGhvcjpgIG5hbWUgaW4NCnRoZSBZQU1MIGhlYWRlciBhdCB0aGUgdG9wIG9mIHRoaXMgZG9jdW1lbnQgc28geW91IGNhbiB0YWtlIGNyZWRpdCBmb3INCnlvdXIgaGFyZCB3b3JrIQ0KDQohW10oaW1nL2tuaXQtcHVibGlzaC5wbmcpe3dpZHRoPSI4MCUifQ0KDQpDb25ncmF0dWxhdGlvbnMgLSB5b3UndmUgY29tcGxldGVkIHRoZSBmaXJzdCBtYWNoaW5lIGxlYXJuaW5nIGNhc2UNCnN0dWR5IQ0K