The link to the dashboard described in this case study is here.

To access the GitHub Repository for this case study see here: https://github.com/opencasestudies/ocs-bp-school-shootings-dashboard/.

You may also access and download the data using our OCSdata package. To learn more about this package including examples, see this link. Here is how you would install this package:

install.packages("OCSdata")

This case study is part of a series of public health case studies for the Bloomberg American Health Initiative.

For users or instructors who only wish to look at the basics of how to create a dashboard in R with the flexdashboard package, please see the Dashboard Basics Section.

Disclaimer: The purpose of the Open Case Studies project is to demonstrate the use of various data science methods, tools, and software in the context of messy, real-world data. A given case study does not cover all aspects of the research process, is not claiming to be the most appropriate way to analyze a given data set, and should not be used in the context of making policy decisions without external consultation from scientific experts.

This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 (CC BY-NC 3.0) United States License.

To cite this case study please use:

Wright, Carrie and Ontiveros, Michael, and Meng, Qier and Jager, Leah and Taub, Margaret and Hicks, Stephanie. (2020). https://github.com//opencasestudies/ocs-bp-school-shootings-dashboard. Open Case Studies: School Shootings in the United States (Version v1.0.0).

The total reading time for this case study is calculated via koRpus and shown below:

Reading Time Method
110 minutes koRpus

Readability Score:

A readability index estimates the reading difficulty level of a particular text. Flesch-Kincaid, FORCAST, and SMOG are three common readability indices that were calculated for this case study via koRpus. These indices provide an estimation of the minimum reading level required to comprehend this case study by grade and age.

Text language: en 
index grade age
Flesch-Kincaid 9 14
FORCAST 10 15
SMOG 11 16

Please help us by filling out our survey.

Motivation


This case study is motivated by this article:

Flannery, D. J., Modzeleski, W. & Kretschmar, J. M. Violence and School Shootings. Curr Psychiatry Rep 15, 331 (2013). DOI: 10.1007/s11920-012-0331-6

The article explores characteristics of school shootings and violence in schools and discusses why these events may occur, as well as their impact on the communities in which they occur.

The article also states that the shooters are most commonly white males, but that many previous studies of shooter characteristics could not identify any particular “profile” of shooters.

“To date, studies of school shootings have concluded that no consistent and reliable profile of school shooters exist…”

However previous studies note some commonalities such as:

“…most shooters were depressed, had experienced some significant loss, felt persecuted or bullied by others, and had prior difficulty coping or had previously tried suicide.”

Therefore in our dashboard we will examine how often a shooter was male or attempted or committed suicide during an event.

Photo by Joshua Hoehne on Unsplash

"School shootings are not all the same and may require different approaches to prevention and treatment, especially with respect to identifying risk factors at the individual, school or community levels, and particularly with regard to examining the role that mental health issues may play to increase risk for perpetration.

The field needs to know more about shooting incidents that are averted, those that result in injury but not death and about the characteristics of the more common occurrence of single homicide school shootings."

Photo by Andre Hunter on Unsplash

Given this need for more research to better understand why these events occur and how they could be averted, in this case study we will demonstrate how to create a resource for others to more easily and interactively access data about school shootings. To do so we will create what is called a dashboard, which is a website that displays a report for a database. Dashboards summarize the data in a database and typically allow for users to interact with the data in some way.

Here you can see an example of a dashboard created in R about downloads of packages on CRAN.

On the website the tabs and plots are interactive. The above dashboard allows for users to get to know the data in a simple and quick way.

The data about package downloads is succinctly summarized in an impactful manner.

We can quickly get a sense that the magrittr package is among the top most widely downloaded packages on CRAN.

[source]

Now let’s learn how to create a dashboard with our data of interest.

Main Questions


Our main questions:

  1. What has been the yearly rate of school shootings and where in the country have they occurred in the last 50 years (from January 1970 to June 2020)?

  2. How many individuals are typically killed in a school shooting?

  3. What were the characteristics of the shooters: How often was a shooter male? How often did a shooter attempt or commit suicide?

Learning Objectives


In this case study, we will demonstrate how to create a dashboard, which is a website that displays a report about a database. In doing so, we will focus on packages and functions from the tidyverse for the data wrangling and visualization sections. The tidyverse is a library of packages created by RStudio. While some students may be familiar with previous R programming packages, these packages make data science in R more human-readable and intuitive.

The skills, methods, and concepts that students will be familiar with by the end of this case study are:

Data Science Learning Objectives:

  1. Importing text from a Google Sheets document (googlesheets4)
  2. Converting date formats (lubridate)
  3. Geocoding data (ggmap) and creating a jitter for geocoded data on a map (SF)
  4. How to reshape data by pivoting between “long” and “wide” formats and drop rows with NA values (tidyr)
  5. How to create data visualizations with ggplot2
  6. An introduction to the basics of R Markdown
  7. How to create an interactive table (DT)
  8. How to create a map (leaflet)
  9. How to create an interactive dashboard with flexdashboard and shiny

Statistical Learning Objectives:

  1. Calculating percentages for data with missing values
  2. Creating summary statistics

Note: statistics is a part of data science


We will begin by loading the packages that we will need:

library(here)
library(readr)
library(googlesheets4)
library(tibble)
library(dplyr)
library(stringr)
library(magrittr)
library(tidyr)
library(ggmap)
library(sf)
library(lubridate)
library(DT)
library(htmltools)
library(ggplot2)
library(forcats)
library(ggforce)
library(waffle)
library(poliscidata)
library(flexdashboard)
library(shiny)
library(leaflet)
library(maps)
library(vembedr)
library(OCSdata)

Note some of these packages are part of the tidyverse and can be loaded together like so:

library(tidyverse)

Packages used in this case study:

Package Use in this case study
here to easily load and save data
readr to import the data as a csv file
googlesheets4 to import directly from Google Sheets
tibble to create tibbles (the tidyverse version of dataframes)
dplyr to filter, subset, join, add rows to, and modify the data
stringr to manipulate character strings within the data (collapsing strings together, replace values, and detect values)
magrittr to pipe sequential commands
tidyr to change the shape or format of tibbles to wide and long, to drop rows with NA values, and to see the last few columns of a tibble
ggmap to geocode the data (which means get the latitude and longitude values)
sf to modify the geocoded data so that overlapping points did not overlap
lubridate to work with the data-time data
DT to create the interactive table
htmltools to add a caption to our interactive table
ggplot2 to create plots
ggforce to create a plot zoom
forcats to reorder factor for plot
waffle to make waffle proportion plots
poliscidata to get population values for the states
flexdashboard to create the dashboard
shiny to allow our dashboard to be interactive
leaflet to implement the leaflet (a JavaScript library for maps) to create the map for our dashboard
maps to create the simple leaflet map example
vembedr to include a video in our case study
OCSdata to access and download OCS data files

The first time we use a function, we will use the :: to indicate which package we are using. Unless we have overlapping function names, this is not necessary, but we will include it here to be informative about where the functions we will use come from.

Context


School shootings get a lot of attention in the the media, but it would be helpful to see all the data on them at once to better understand them. A dashboard can help with this, so that people get a boarder understanding of the issue rather than hearing about singular specific incidences from the media.

In addition to injuries and deaths, shooting events can also have broad and lasting impacts for those who witness but are not directly involved.

According to the Center for Injury Research and Prevention at the Children’s Hospital of Philadelphia:

The most common shootings on school grounds rarely involve large numbers of victims, but even a shooting of just one student at school has ramifications far beyond those directly involved.

Students and staff that witness school shootings are likely to suffer from traumatic stress symptoms, become anxious or depressed and have general concerns about their safety.

While many witnesses will have temporary symptoms, others will be symptomatic for a much longer period of time and even develop chronic psychiatric disorders.

Even short-term impairments can cause severe distress and have profound effects on academic achievement and the social and emotional growth of impacted students.

Furthermore, school shootings can have vast and lasting impacts because many students can witness a single event.

Another recently published article indicates that:

Over 240,000 American students experienced a school shooting in the last two decades.

[source]

This study followed students who experienced a school shooting the United States between 2008 and 2013 and assessed their mental well-being. They found that:

Fatal school shootings have large and persistent impacts on the mental health of local youth. In the two years following a fatal school shooting, the monthly number of antidepressant prescriptions written to individuals under age 20 is 21.3 percent higher in the shooting-exposed relative to the reference areas.

Rossin-Slater, M., Schnell, M., Schwandt, H., Trejo, S. & Uniat, L. Local Exposure to School Shootings and Youth Antidepressant Use. w26563 http://www.nber.org/papers/w26563.pdf (2019) doi:10.3386/w26563.

Thus, it is useful to better understand the characteristics of these shootings. Having better data on what they look like nationwide can help with identifying associations of shootings with key characteristics. Better descriptive information such as this may then lead to more knowledge about factors that predict school shootings, which could help develop preventive interventions. This way, we might not only prevent the direct involvement of students in future events, but also to prevent students and staff from witnessing these events.

Photo by Fernando @cferdo on Unsplash

Limitations


There are some important considerations regarding this data analysis to keep in mind:

This dashboard only uses one source of data. There may be school shooting events that are not listed in this data or errors in this data.

According to the database website itself:

“This database was developed from open-source information and may include reporting errors.”

Furthermore, according to this article, schools in 2013, schools were not required to report school shootings unless they resulted in a suicide or homicide. Therefore there may be more events that result in only injury or no injuries or death that may not be included.

There are indeed events in the dataset that include zero deaths and zero injuries, but it is very likely that many of these events are not listed.

What are the data?


We will use data from the open-source K-12 Shool Shooting Database from the Center for Homeland Defense and Security at the Naval Postgraduate School(NPS) in Monterey, California. This data is updated daily. The data used in this case study was downloaded in June of 2020.

Riedman, David, and Desmond O’Neill. “CHDS – K-12 School Shooting Database.” Center for Homeland Defense and Security, June 2020, www.chds.us/ssdb.

This database includes information about school shooting events for students in grades K-12 in the United States dating back to 1970. The database has additional information not shown on our dashboard including but not limited to:

  • Location of the event at the school
  • If the event occurred during a sporting event
  • Time of day of the event
  • Day of the week of the event
  • Source for the shooting information
  • If the event was pre-planned or not
  • Shooter’s actions immediately following the shooting
  • Shooter characteristics (affiliation with the school, if they had accomplices, if they took hostages, and their age and race)
  • Victim characteristics (affiliation with the school, if they were targeted, their age and race)

According to the K-12 Shool Shooting Database website:

The School Shooting Database Project is conducted as part of the Advanced Thinking in Homeland Security (HSx) program at the Naval Postgraduate School’s Center for Homeland Defense and Security (CHDS).

The database compiles information from more than 25 different sources including peer-reviewed studies, government reports, mainstream media, non-profits, private websites, blogs, and crowd-sourced lists that have been analyzed, filtered, deconflicted, and cross-referenced. All of the information is based on open-source information and 3rd party reporting.

Data Import


Previously, the website for this data was located at “https://www.chds.us/ssdb/dataset/” (which is no longer an active link), which displayed an active Google Sheets document and a link to download a csv file of the data. At the time that we created this case study (June of 2020) we downloaded the data from this website.

Now the data can be found at this link and a file of the raw data can be downloaded by clicking the “DOWNLOAD RAW DATA” button. This file was previously a .csv file, but it is now an .xlsx file.

To account for changes with this website, we have made the previous .csv file available for you to download using the OCSdata package:

# library(OCSdata)
raw_data("ocs-bp-school-shootings-dashboard", outpath = getwd())

If you have trouble using the package, you may also download this .csv file here.

In our case, we downloaded this data and put it within a “raw” subdirectory of a “data” directory for our project. If you use an RStudio project, then you can use the here() function from the here package to make the path for importing this data simpler. The here package automatically starts looking for files based on where you have a .Rproj file which is created when you start a new RStudio project. We can specify that we want to look for the "K-12_SSDB_(Public)-K-12_SSDB_(Public)_Linked.csv" file within the “raw” directory within the “data” directory within a directory where our .Rproj file is located by separating the names of these directories using commas and listing “data” first.

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects.

We can import the raw .csv file using the read_csv() function from the readr package. Let’s start by only importing the first five rows with the n_max argument which is the max number of rows to read in from the file. By doing this, we can check for errors before reading in the entire file. Note that you would need to modify the file argument if you set your data files up differently.

shooting_data <- 
  readr::read_csv(file = 
                    here::here("data", "raw",
                         "K-12_SSDB_(Public)-K-12_SSDB_(Public)_Linked.csv"), 
                  n_max = 5)
shooting_data
# A tibble: 5 x 47
  `Updated 6/2/202~` ...2  ...3  ...4  ...5  ...6  ...7  ...8  ...9  ...10 ...11
  <chr>              <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Date               Scho~ City  State Reli~ Kill~ Woun~ Tota~ Gend~ Vict~ Vict~
2 1/5/1970           Hine~ Wash~ DC    3     1     0     1     Male  Stud~ 15   
3 1/5/1970           Sous~ Wash~ DC    3     0     1     1     Male  Stud~ <NA> 
4 1/5/1970           Unna~ Wash~ DC    2     0     0     0     No V~ No V~ <NA> 
5 2/6/1970           John~ Clev~ OH    2     0     1     1     Male  Stud~ 18   
# ... with 36 more variables: ...12 <chr>, ...13 <chr>, ...14 <chr>,
#   ...15 <chr>, ...16 <chr>, ...17 <chr>, ...18 <chr>, ...19 <chr>,
#   ...20 <chr>, ...21 <chr>, ...22 <chr>, ...23 <chr>, ...24 <chr>,
#   ...25 <chr>, ...26 <chr>, ...27 <chr>, ...28 <chr>, ...29 <chr>,
#   ...30 <chr>, ...31 <chr>, ...32 <chr>, ...33 <chr>, ...34 <chr>,
#   ...35 <chr>, ...36 <chr>, ...37 <chr>, ...38 <chr>, ...39 <chr>,
#   ...40 <chr>, ...41 <chr>, ...42 <chr>, ...43 <chr>, ...44 <chr>, ...

We see the first row is a sentence that states:

“Updated 6/2/2020 - View graphs and research methodology on www.chds.us/ssdb If you have information about other incidents, please email .”

We do not need this information, so we can skip it using the skip argument of read_csv() function. Specifically, we specify that we wish to only skip 1 row with skip = 1. We can also specify that the next row should be used for column names using the col_names = TRUE argument.

shooting_data <- 
  readr::read_csv(file = here::here("data", "raw",
                                    "K-12_SSDB_(Public)-K-12_SSDB_(Public)_Linked.csv"), 
                  col_names = TRUE, skip = 1)

We can use the glimpse function from the dplyr package to take a look at columns within the database:

# Scroll through the output!
dplyr::glimpse(shooting_data)
Rows: 1,556
Columns: 47
$ Date                                                                                                                                                 <chr> ~
$ School                                                                                                                                               <chr> ~
$ City                                                                                                                                                 <chr> ~
$ State                                                                                                                                                <chr> ~
$ `Reliability Score (1-5)`                                                                                                                            <dbl> ~
$ `Killed (includes shooter)`                                                                                                                          <dbl> ~
$ Wounded                                                                                                                                              <dbl> ~
$ `Total Injured/Killed Victims`                                                                                                                       <dbl> ~
$ `Gender of Victims (M/F/Both)`                                                                                                                       <chr> ~
$ `Victim's Affiliation w/ School`                                                                                                                     <chr> ~
$ `Victim's age(s)`                                                                                                                                    <dbl> ~
$ `Victims Race`                                                                                                                                       <chr> ~
$ `Victim Ethnicity`                                                                                                                                   <chr> ~
$ `Targeted Specific Victim(s)`                                                                                                                        <chr> ~
$ `Random Victims`                                                                                                                                     <chr> ~
$ `Bullied (Y/N/ N/A)`                                                                                                                                 <chr> ~
$ `Domestic Violence (Y/N)`                                                                                                                            <chr> ~
$ `Suicide (Shooter was only victim) Y/N/ N/A`                                                                                                         <chr> ~
$ `Suicide (shot self immediately following initial shootings) Y/N/ N/A`                                                                               <chr> ~
$ `Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A` <chr> ~
$ `Suicide (or attempted suicide) by Shooter (Y/N)`                                                                                                    <chr> ~
$ `Shooter's actions immediately after shots fired`                                                                                                    <chr> ~
$ `Pre-planned school attack`                                                                                                                          <chr> ~
$ Summary                                                                                                                                              <chr> ~
$ Category                                                                                                                                             <chr> ~
$ `School Type`                                                                                                                                        <chr> ~
$ `Narrative (Detailed Summary/ Background)`                                                                                                           <chr> ~
$ Sources                                                                                                                                              <chr> ~
$ `Time of Occurrence (12 hour AM/PM)`                                                                                                                 <time> ~
$ `Duration (minutes)`                                                                                                                                 <dbl> ~
$ `Day of week (formula)`                                                                                                                              <chr> ~
$ `During School Day (Y/N)`                                                                                                                            <chr> ~
$ `Time Period`                                                                                                                                        <chr> ~
$ `During a Sporting Event (Y/N)`                                                                                                                      <chr> ~
$ `During a school sponsored event (school dance, concert, play, activity)`                                                                            <chr> ~
$ Location                                                                                                                                             <chr> ~
$ `Number of Shots Fired`                                                                                                                              <dbl> ~
$ `Firearm Type`                                                                                                                                       <chr> ~
$ `Number of Shooters`                                                                                                                                 <dbl> ~
$ `Shooter Name`                                                                                                                                       <chr> ~
$ `Shooter Age`                                                                                                                                        <dbl> ~
$ `Shooter Gender`                                                                                                                                     <chr> ~
$ Race                                                                                                                                                 <chr> ~
$ `Shooter Ethnicity`                                                                                                                                  <chr> ~
$ `Shooter's Affiliation with School`                                                                                                                  <chr> ~
$ `Shooter had an accomplice who did not fire gun (Y/N)`                                                                                               <chr> ~
$ `Hostages Taken (Y/N)`                                                                                                                               <chr> ~

We can also use the utils str() function, which is short for “structure” to see more details about the internal structure of the data. Therefore, the str() function will give us more information about the actual values for each column within the data, not just the columns themselves. Typically we would be able to see some of the values with glimpse() function as well, but some of the columns have very long names, thus obscuring the first few values in the output.

# Scroll through the output!
str(shooting_data)
spec_tbl_df [1,556 x 47] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Date                                                                                                                                              : chr [1:1556] "1/5/1970" "1/5/1970" "1/5/1970" "2/6/1970" ...
 $ School                                                                                                                                            : chr [1:1556] "Hine Junior High School" "Sousa Junior High" "Unnamed High School" "John F. Kennedy High School" ...
 $ City                                                                                                                                              : chr [1:1556] "Washington" "Washington" "Washington" "Cleveland" ...
 $ State                                                                                                                                             : chr [1:1556] "DC" "DC" "DC" "OH" ...
 $ Reliability Score (1-5)                                                                                                                           : num [1:1556] 3 3 2 2 2 3 3 2 2 2 ...
 $ Killed (includes shooter)                                                                                                                         : num [1:1556] 1 0 0 0 0 1 1 0 0 0 ...
 $ Wounded                                                                                                                                           : num [1:1556] 0 1 0 1 2 5 0 1 2 1 ...
 $ Total Injured/Killed Victims                                                                                                                      : num [1:1556] 1 1 0 1 2 6 1 1 2 1 ...
 $ Gender of Victims (M/F/Both)                                                                                                                      : chr [1:1556] "Male" "Male" "No Victims" "Male" ...
 $ Victim's Affiliation w/ School                                                                                                                    : chr [1:1556] "Student" "Student" "No Victims" "Student" ...
 $ Victim's age(s)                                                                                                                                   : num [1:1556] 15 NA NA 18 NA NA 18 19 NA 15 ...
 $ Victims Race                                                                                                                                      : chr [1:1556] NA NA "No Victims" NA ...
 $ Victim Ethnicity                                                                                                                                  : chr [1:1556] NA NA "No Victims" NA ...
 $ Targeted Specific Victim(s)                                                                                                                       : chr [1:1556] "No" "No" "Yes" "Yes" ...
 $ Random Victims                                                                                                                                    : chr [1:1556] "Yes" "Yes" "No" "No" ...
 $ Bullied (Y/N/ N/A)                                                                                                                                : chr [1:1556] "No" "No" "No" "No" ...
 $ Domestic Violence (Y/N)                                                                                                                           : chr [1:1556] "No" "No" "No" "No" ...
 $ Suicide (Shooter was only victim) Y/N/ N/A                                                                                                        : chr [1:1556] "N/A" "N/A" "N/A" "N/A" ...
 $ Suicide (shot self immediately following initial shootings) Y/N/ N/A                                                                              : chr [1:1556] "N/A" "N/A" "N/A" "N/A" ...
 $ Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A: chr [1:1556] "N/A" "N/A" "N/A" "N/A" ...
 $ Suicide (or attempted suicide) by Shooter (Y/N)                                                                                                   : chr [1:1556] "No" "No" "No" "No" ...
 $ Shooter's actions immediately after shots fired                                                                                                   : chr [1:1556] "Unknown if Subdued Surrendered or Fled" "Immediately Surrendered" "Fled" "Unknown if Subdued Surrendered or Fled" ...
 $ Pre-planned school attack                                                                                                                         : chr [1:1556] "No" "No" "No" "No" ...
 $ Summary                                                                                                                                           : chr [1:1556] "Didn't know how to operate pistol, cocked hammer and couldn't get it to safely release causing accidental discharge" "Occurred during horseplay in the school" "Student shot at twice during attempted robbery on playground" "Argument in school hallway escalated into shooting" ...
 $ Category                                                                                                                                          : chr [1:1556] "Accidental" "Accidental" "Robbery" "Escalation of Dispute" ...
 $ School Type                                                                                                                                       : chr [1:1556] "High" "Junior High" "High" "High" ...
 $ Narrative (Detailed Summary/ Background)                                                                                                          : chr [1:1556] "Student showing off gun cocked hammer and could not get it to release causing accidental discharge and killing "| __truncated__ "14YOM student shot during \"horseplay\" in the school hallway. Friend of the victim surrendered to police." "Group of 10 teens attempted to rob 16YOM (James Owens) on school playground. When victim ran, unknown teen susp"| __truncated__ "Argument between shooter and victim escalated into shooting in school hallway." ...
 $ Sources                                                                                                                                           : chr [1:1556] "https://news.google.com/newspapers?id=AfRYAAAAIBAJ&pg=3025,1894998" "https://news.google.com/newspapers?id=AfRYAAAAIBAJ&pg=3025,1894998 https://www.newspapers.com/image/156467116/?"| __truncated__ "https://www.newspapers.com/image/156467116/?terms=school%2Bshooting" "https://www.newspapers.com/image/18059538/?terms=school%2Bshooting" ...
 $ Time of Occurrence (12 hour AM/PM)                                                                                                                : 'hms' num [1:1556] NA NA NA NA ...
  ..- attr(*, "units")= chr "secs"
 $ Duration (minutes)                                                                                                                                : num [1:1556] 1 1 1 1 NA 8 1 1 NA 1 ...
 $ Day of week (formula)                                                                                                                             : chr [1:1556] "Mon" "Mon" "Mon" "Fri" ...
 $ During School Day (Y/N)                                                                                                                           : chr [1:1556] "Yes" "Yes" "Yes" "Yes" ...
 $ Time Period                                                                                                                                       : chr [1:1556] NA NA NA NA ...
 $ During a Sporting Event (Y/N)                                                                                                                     : chr [1:1556] "No" "No" "No" "No" ...
 $ During a school sponsored event (school dance, concert, play, activity)                                                                           : chr [1:1556] "No" "No" "No" "No" ...
 $ Location                                                                                                                                          : chr [1:1556] "Inside School Building" "Inside School Building" "Outside on School Property" "Inside School Building" ...
 $ Number of Shots Fired                                                                                                                             : num [1:1556] 1 1 2 4 NA NA 1 1 2 NA ...
 $ Firearm Type                                                                                                                                      : chr [1:1556] "Handgun" "Handgun" "Handgun" "Handgun" ...
 $ Number of Shooters                                                                                                                                : num [1:1556] 1 1 1 1 2 8 1 1 1 2 ...
 $ Shooter Name                                                                                                                                      : chr [1:1556] "Minor" "Minor" "Unknown" "Gertis J. Perry" ...
 $ Shooter Age                                                                                                                                       : num [1:1556] 15 NA NA 18 NA NA 16 18 15 NA ...
 $ Shooter Gender                                                                                                                                    : chr [1:1556] "Male" "Male" "Male" "Male" ...
 $ Race                                                                                                                                              : chr [1:1556] NA NA NA NA ...
 $ Shooter Ethnicity                                                                                                                                 : chr [1:1556] NA NA NA "Not Hispanic or Latino" ...
 $ Shooter's Affiliation with School                                                                                                                 : chr [1:1556] "Student" "Student" "Student" "Student" ...
 $ Shooter had an accomplice who did not fire gun (Y/N)                                                                                              : chr [1:1556] "Yes" "No" "Yes" "No" ...
 $ Hostages Taken (Y/N)                                                                                                                              : chr [1:1556] "No" "No" "No" "No" ...
 - attr(*, "spec")=
  .. cols(
  ..   Date = col_character(),
  ..   School = col_character(),
  ..   City = col_character(),
  ..   State = col_character(),
  ..   `Reliability Score (1-5)` = col_double(),
  ..   `Killed (includes shooter)` = col_double(),
  ..   Wounded = col_double(),
  ..   `Total Injured/Killed Victims` = col_double(),
  ..   `Gender of Victims (M/F/Both)` = col_character(),
  ..   `Victim's Affiliation w/ School` = col_character(),
  ..   `Victim's age(s)` = col_double(),
  ..   `Victims Race` = col_character(),
  ..   `Victim Ethnicity` = col_character(),
  ..   `Targeted Specific Victim(s)` = col_character(),
  ..   `Random Victims` = col_character(),
  ..   `Bullied (Y/N/ N/A)` = col_character(),
  ..   `Domestic Violence (Y/N)` = col_character(),
  ..   `Suicide (Shooter was only victim) Y/N/ N/A` = col_character(),
  ..   `Suicide (shot self immediately following initial shootings) Y/N/ N/A` = col_character(),
  ..   `Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A` = col_character(),
  ..   `Suicide (or attempted suicide) by Shooter (Y/N)` = col_character(),
  ..   `Shooter's actions immediately after shots fired` = col_character(),
  ..   `Pre-planned school attack` = col_character(),
  ..   Summary = col_character(),
  ..   Category = col_character(),
  ..   `School Type` = col_character(),
  ..   `Narrative (Detailed Summary/ Background)` = col_character(),
  ..   Sources = col_character(),
  ..   `Time of Occurrence (12 hour AM/PM)` = col_time(format = ""),
  ..   `Duration (minutes)` = col_double(),
  ..   `Day of week (formula)` = col_character(),
  ..   `During School Day (Y/N)` = col_character(),
  ..   `Time Period` = col_character(),
  ..   `During a Sporting Event (Y/N)` = col_character(),
  ..   `During a school sponsored event (school dance, concert, play, activity)` = col_character(),
  ..   Location = col_character(),
  ..   `Number of Shots Fired` = col_double(),
  ..   `Firearm Type` = col_character(),
  ..   `Number of Shooters` = col_double(),
  ..   `Shooter Name` = col_character(),
  ..   `Shooter Age` = col_double(),
  ..   `Shooter Gender` = col_character(),
  ..   Race = col_character(),
  ..   `Shooter Ethnicity` = col_character(),
  ..   `Shooter's Affiliation with School` = col_character(),
  ..   `Shooter had an accomplice who did not fire gun (Y/N)` = col_character(),
  ..   `Hostages Taken (Y/N)` = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 

We can see from this that many of the variables have Yes or No values, while others have relatively long descriptions. You may also notice that the State values are state abbreviations, not full state names. This is something that we will add to the data later.

Alternatively, if we wanted to make a dashboard that continually updated as data got updated, we could do the following to import the data directly from a live Google Sheets document as previously this was available for this data.

To do so we would use the read_sheet() function from the googlesheets4 package. Typically authentication is required, (meaning that you would need to sign in with your Google account using a username and or password), but since this was a public sheet we did not need to worry about authentication. To avoid being asked about this we used the gs4_deauth() function which puts the package into a de-authorized state that will not ask for users to sign in.

googlesheets4::gs4_deauth()

Great, now we would need to get the shared link from the document. We could previously do so by clicking on the link to the actually Google Sheets document like so:

Then we can click on the “share” button to get access to the link:

Finally we can click on “copy link” button to copy the link:

Once you copy a link like this, you can use the read_sheet() function to import the data by simply pasting the link in quotes, like so:

data_url <- "https://docs.google.com/spreadsheets/d/1HqbfMxnk9X3_mQvLyW_LEUe3Yyr7cXMPfwqUVfdq7sY/edit?usp=sharing"

googlesheet_data <- 
  read_sheet(data_url)

This is a great option, however, we chose not to do this for this case study to allow this tutorial to be more easily maintained over time. This was evidently a good choice since the data is no longer accessible in the same way.

To allow users to skip import we will save the data as an RDA file:

save(shooting_data, file = here::here("data", "imported", "shooting_data.rda"))

Data Exploration and Wrangling


If you have been following along but stopped, we could load our imported data like so:

load(here::here("data", "imported", "shooting_data.rda"))

If you skipped the data import section click here.

First you need to install the OCSdata package:

install.packages("OCSdata")

Then, you may download the imported data .rda file using the following function:

# library(OCSdata)
imported_data("ocs-bp-school-shootings-dashboard", outpath = getwd())
# load(here::here("OCSdata", "data", "imported", "shooting_data.rda"))

To load the downloaded data into your environment, you may double click on the .rda file in RStudio or using the load() function.

If the package does not work for you, an RDA file (stands for R data) of the data can be found here or slightly more directly here. Download this file and then place it in your current working directory. We recommend using an RStudio project and the here package to navigate to your file more easily.

We have put this file in a directory called “imported” within a directory called “data” within our working directory (which has a .Rproj file).

load(here::here("data", "imported", "shooting_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects.


Luckily, our data is already in pretty good shape, but we want to make our data more useful for our dashboard.

Adding state name


It would be useful to have the full state name in our data, rather than just the abbreviation.

We can do so by using data related to the US 50 states in a dataset called state that is automatically loaded with R sessions in the datasets package. The state.abb object is a list of the state abbreviations and state.name is a list of the state names.

state.abb
 [1] "AL" "AK" "AZ" "AR" "CA" "CO" "CT" "DE" "FL" "GA" "HI" "ID" "IL" "IN" "IA"
[16] "KS" "KY" "LA" "ME" "MD" "MA" "MI" "MN" "MS" "MO" "MT" "NE" "NV" "NH" "NJ"
[31] "NM" "NY" "NC" "ND" "OH" "OK" "OR" "PA" "RI" "SC" "SD" "TN" "TX" "UT" "VT"
[46] "VA" "WA" "WV" "WI" "WY"
state.name
 [1] "Alabama"        "Alaska"         "Arizona"        "Arkansas"      
 [5] "California"     "Colorado"       "Connecticut"    "Delaware"      
 [9] "Florida"        "Georgia"        "Hawaii"         "Idaho"         
[13] "Illinois"       "Indiana"        "Iowa"           "Kansas"        
[17] "Kentucky"       "Louisiana"      "Maine"          "Maryland"      
[21] "Massachusetts"  "Michigan"       "Minnesota"      "Mississippi"   
[25] "Missouri"       "Montana"        "Nebraska"       "Nevada"        
[29] "New Hampshire"  "New Jersey"     "New Mexico"     "New York"      
[33] "North Carolina" "North Dakota"   "Ohio"           "Oklahoma"      
[37] "Oregon"         "Pennsylvania"   "Rhode Island"   "South Carolina"
[41] "South Dakota"   "Tennessee"      "Texas"          "Utah"          
[45] "Vermont"        "Virginia"       "Washington"     "West Virginia" 
[49] "Wisconsin"      "Wyoming"       

We will combine these using the tibble() function from the tibble() package.

state_df <- 
  tibble(State_abb = state.abb, 
         State = state.name)

slice_head(state_df, n=4)
# A tibble: 4 x 2
  State_abb State   
  <chr>     <chr>   
1 AL        Alabama 
2 AK        Alaska  
3 AZ        Arizona 
4 AR        Arkansas

Now we will combine this with our data using the left_join() function from the dplyr package. There are several ways to join data using the dplyr package.

[source]

Here is a visualization of these options:

[source]

See here for more details about joining data.

We probably have data for all fifty states, but there may not have been school shootings in all 50 states in this dataset, therefore we don’t want to use the full_join() function.

We also don’t want the inner_join() function because DC does not have a state name. According to Wikipedia:

The U.S. Constitution provides for a federal district under the exclusive jurisdiction of Congress; the district is therefore not a part of any U.S. state (nor is it one itself)

Thus we will use the left_join(x,y) function where x in this case will be the shooting_data (as it is introduced to this code first through the %<>% compound assignment pipe operator) and y is the state_df. Thus, we add the state_df values where they match to the shooting_data.

The %<>% compound operator allows us to use the an input and reassign it at the end after all the subsequent steps have been performed. We can therefore use data_input %<>% instead of data_input <- data_input %>%. We will demonstrate this in the code below.

shooting_data %<>%
  rename("State_abb" = State) %>%
  left_join(state_df, by = c("State_abb" = "State_abb"))

In contrast, we can just use the %>% pipe operator to select a set of columns and peek at the first four rows of the new data frame.

Click here if you are unfamiliar with piping in R, which uses this %>% operator.

By piping we mean using the %>% pipe operator which is accessible after loading the tidyverse or several of the packages within the tidyverse like dplyr because they load the magrittr package. This allows us to perform multiple sequential steps on one data input. The object on the left side is used as input to any commands to the right or below.

shooting_data %>%
  select(School, City, State_abb, State) %>%
  slice_head(n = 4)
# A tibble: 4 x 4
  School                      City       State_abb State
  <chr>                       <chr>      <chr>     <chr>
1 Hine Junior High School     Washington DC        <NA> 
2 Sousa Junior High           Washington DC        <NA> 
3 Unnamed High School         Washington DC        <NA> 
4 John F. Kennedy High School Cleveland  OH        Ohio 

Reformatting dates


We also want to reformat our date values and create a Date_year variable based on the year in each date. We can use the lubridate package for this.

The mdy() function converts dates into a format where dates are listed as month, date, and year with hyphens in between. The year() function can then be used to extract just the year from each date.

shooting_data %<>%
  mutate(Date = lubridate::mdy(Date)) %>%
  mutate(Date_year = lubridate::year(Date))

shooting_data %>% 
  select(Date, Date_year)
# A tibble: 1,556 x 2
   Date       Date_year
   <date>         <dbl>
 1 1970-01-05      1970
 2 1970-01-05      1970
 3 1970-01-05      1970
 4 1970-02-06      1970
 5 1970-03-23      1970
 6 1970-04-15      1970
 7 1970-04-22      1970
 8 1970-05-08      1970
 9 1970-05-15      1970
10 1970-08-28      1970
# ... with 1,546 more rows

Looks good!

Reformatting data types


If you recall, in our dataset we have many variables that have either Yes or No values or Y and N values.

names(shooting_data)
 [1] "Date"                                                                                                                                              
 [2] "School"                                                                                                                                            
 [3] "City"                                                                                                                                              
 [4] "State_abb"                                                                                                                                         
 [5] "Reliability Score (1-5)"                                                                                                                           
 [6] "Killed (includes shooter)"                                                                                                                         
 [7] "Wounded"                                                                                                                                           
 [8] "Total Injured/Killed Victims"                                                                                                                      
 [9] "Gender of Victims (M/F/Both)"                                                                                                                      
[10] "Victim's Affiliation w/ School"                                                                                                                    
[11] "Victim's age(s)"                                                                                                                                   
[12] "Victims Race"                                                                                                                                      
[13] "Victim Ethnicity"                                                                                                                                  
[14] "Targeted Specific Victim(s)"                                                                                                                       
[15] "Random Victims"                                                                                                                                    
[16] "Bullied (Y/N/ N/A)"                                                                                                                                
[17] "Domestic Violence (Y/N)"                                                                                                                           
[18] "Suicide (Shooter was only victim) Y/N/ N/A"                                                                                                        
[19] "Suicide (shot self immediately following initial shootings) Y/N/ N/A"                                                                              
[20] "Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A"
[21] "Suicide (or attempted suicide) by Shooter (Y/N)"                                                                                                   
[22] "Shooter's actions immediately after shots fired"                                                                                                   
[23] "Pre-planned school attack"                                                                                                                         
[24] "Summary"                                                                                                                                           
[25] "Category"                                                                                                                                          
[26] "School Type"                                                                                                                                       
[27] "Narrative (Detailed Summary/ Background)"                                                                                                          
[28] "Sources"                                                                                                                                           
[29] "Time of Occurrence (12 hour AM/PM)"                                                                                                                
[30] "Duration (minutes)"                                                                                                                                
[31] "Day of week (formula)"                                                                                                                             
[32] "During School Day (Y/N)"                                                                                                                           
[33] "Time Period"                                                                                                                                       
[34] "During a Sporting Event (Y/N)"                                                                                                                     
[35] "During a school sponsored event (school dance, concert, play, activity)"                                                                           
[36] "Location"                                                                                                                                          
[37] "Number of Shots Fired"                                                                                                                             
[38] "Firearm Type"                                                                                                                                      
[39] "Number of Shooters"                                                                                                                                
[40] "Shooter Name"                                                                                                                                      
[41] "Shooter Age"                                                                                                                                       
[42] "Shooter Gender"                                                                                                                                    
[43] "Race"                                                                                                                                              
[44] "Shooter Ethnicity"                                                                                                                                 
[45] "Shooter's Affiliation with School"                                                                                                                 
[46] "Shooter had an accomplice who did not fire gun (Y/N)"                                                                                              
[47] "Hostages Taken (Y/N)"                                                                                                                              
[48] "State"                                                                                                                                             
[49] "Date_year"                                                                                                                                         

Note that in this case study, we will mostly be visualizing the data. However, for more intensive analysis, it would be better to make names more tidy, such as using lowercase and no spaces etc.

These are the variables that have Y/N in the name or the Targeted Specific Victim(s), Random Victims, Pre-planned school attack variables.

We can make these consistently TRUE and FALSE by using the case_when() function from the dplyr package. This function allows us to specify new values for existing values.

If you are familiar with the recode() function from dplyr, click here for an explanation of why case_when() is better in this case.

The benefit of the case_when() function, is that changing the values to TRUE or FALSE also results in the class type of the variable changing to type logical (which is interpreted as a binary variable with TRUE and FALSE values) otherwise, with recode() the variables would remain as class type character.


Click here for an explanation about data types in R.

There are several classes of data in R programming. Character is one of these classes. A character string is an individual data value made up of characters. This can be a paragraph, like the legend for the table, or it can be a single letter or number like the letter "a" or the number "3". If data are of class character, than the numeric values will not be processed like a numeric value in a mathematical sense.

If you want your numeric values to be interpreted that way, they need to be converted to a numeric class. The options typically used are integer (which has no decimal place) and double precision (which has a decimal place).

Similarly if your data is of class character and are values of TRUE and FALSE they will be interpreted as two different strings.

However, logical data is interpreted slightly differently where a FALSE value indicates the absence of something, while a TRUE indicates the presence of something.


Click here for more details about the differences between the recode() and case_when() functions.

Note that with recode() there is the option that other values be recoded to NA although this is not the default, however with case_when() other values not explicitly assigned in the case_when() statement will be assigned to NA. Further more only values can be used on the left side when using recode() whereas case_when() accepts expressions.

OK let’s start by looking at the columns of interest by using the select() function and asking for any patterns that match the character string “Y/N” or “Specific” or “Random” or “Pre-planned”.

Formally, we can search for these using the | symbol, which is interpreted as an or, thus any variables that has a name that matches any of these patterns will be changed.

shooting_data %>% 
  select(matches("Y/N|Specific|Random|Pre-planned"))
# A tibble: 1,556 x 13
   `Targeted Specific Victi~` `Random Victims` `Bullied (Y/N/~` `Domestic Viol~`
   <chr>                      <chr>            <chr>            <chr>           
 1 No                         Yes              No               No              
 2 No                         Yes              No               No              
 3 Yes                        No               No               No              
 4 Yes                        No               No               No              
 5 No                         No               No               No              
 6 Yes                        Yes              No               No              
 7 Yes                        No               No               No              
 8 Yes                        No               No               No              
 9 No                         Yes              <NA>             No              
10 Yes                        No               No               No              
# ... with 1,546 more rows, and 9 more variables:
#   `Suicide (Shooter was only victim) Y/N/ N/A` <chr>,
#   `Suicide (shot self immediately following initial shootings) Y/N/ N/A` <chr>,
#   `Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A` <chr>,
#   `Suicide (or attempted suicide) by Shooter (Y/N)` <chr>,
#   `Pre-planned school attack` <chr>, `During School Day (Y/N)` <chr>,
#   `During a Sporting Event (Y/N)` <chr>, ...

We see the Yes and No values. Let’s look closer at one of these columns.

shooting_data %>% 
  count(`Suicide (or attempted suicide) by Shooter (Y/N)`)
# A tibble: 6 x 2
  `Suicide (or attempted suicide) by Shooter (Y/N)`     n
  <chr>                                             <int>
1 N                                                     1
2 N/A                                                  18
3 No                                                 1308
4 Officer Involved                                     26
5 Yes                                                 202
6 <NA>                                                  1

We see there are six different values in this column. To recode this column, we need to consider what we recode all the values.

To implement the case_when() recoding of values, the existing values are written on the left of the ~ sign (quotation marks are necessary around the existing values) and new values are written on the right (quotations marks are not necessary as these are TRUE and FALSE statements).

shooting_data %>%
       select(`Suicide (or attempted suicide) by Shooter (Y/N)`) %>%
       mutate(type = dplyr::case_when(. == "Yes" ~ TRUE,
                                      . == "No" ~ FALSE,
                                      . == "Y" ~ TRUE,
                                      . == "N" ~ FALSE,
                                      . == "Officer Involved" ~ TRUE))
# A tibble: 1,556 x 2
   `Suicide (or attempted suicide) by Shooter (Y/N)` type 
   <chr>                                             <lgl>
 1 No                                                FALSE
 2 No                                                FALSE
 3 No                                                FALSE
 4 No                                                FALSE
 5 No                                                FALSE
 6 No                                                FALSE
 7 No                                                FALSE
 8 No                                                FALSE
 9 No                                                FALSE
10 No                                                FALSE
# ... with 1,546 more rows

In the above code chunk, we did this for one of the columns, but now let’s do for all the columns that matched our string “Y/N|Specific|Random|Pre-planned” as above.

To do this, we will use the across() function from the dplyr package and the matches() function from the tidyselect package to allow us to apply this to all of the variables that have a pattern that that matches any of those of the variables we want to change.

The across() function then applies the case_when() function to all of these variables. Notice that the ~ symbol is necessary before the function that is applied using across().

shooting_data %<>%
  mutate(dplyr::across(.cols = matches("Y/N|Specific|Random|Pre-planned"),
                       ~ dplyr::case_when(. == "Yes" ~ TRUE,
                                          . == "No" ~ FALSE,
                                          . == "Y" ~ TRUE,
                                          . == "N" ~ FALSE,
                                          . == "Officer Involved" ~ TRUE)))

Finally, we can check out what happened after recoding the variables.

shooting_data %>% 
  select(matches("Y/N|Specific|Random|Pre-planned"))
# A tibble: 1,556 x 13
   `Targeted Specific Victi~` `Random Victims` `Bullied (Y/N/~` `Domestic Viol~`
   <lgl>                      <lgl>            <lgl>            <lgl>           
 1 FALSE                      TRUE             FALSE            FALSE           
 2 FALSE                      TRUE             FALSE            FALSE           
 3 TRUE                       FALSE            FALSE            FALSE           
 4 TRUE                       FALSE            FALSE            FALSE           
 5 FALSE                      FALSE            FALSE            FALSE           
 6 TRUE                       TRUE             FALSE            FALSE           
 7 TRUE                       FALSE            FALSE            FALSE           
 8 TRUE                       FALSE            FALSE            FALSE           
 9 FALSE                      TRUE             NA               FALSE           
10 TRUE                       FALSE            FALSE            FALSE           
# ... with 1,546 more rows, and 9 more variables:
#   `Suicide (Shooter was only victim) Y/N/ N/A` <lgl>,
#   `Suicide (shot self immediately following initial shootings) Y/N/ N/A` <lgl>,
#   `Suicide (e.g., shot self at end of incident - time period between first shots and suicide, different location, when confronted by police) Y/N/ N/A` <lgl>,
#   `Suicide (or attempted suicide) by Shooter (Y/N)` <lgl>,
#   `Pre-planned school attack` <lgl>, `During School Day (Y/N)` <lgl>,
#   `During a Sporting Event (Y/N)` <lgl>, ...

Looks good!

Geocoding with the ggmap package

For the purpose of our dashboard, we are interested in creating a map.

To do this, we need to perform a process called geocoding. Geocoding is the process of converting addresses into latitude and longitude coordinates.

To perform the geocoding we need the address of each school in the data set. The data currently does not list the actual address, but does have information about the school where the event occurred.

Since some schools have the same name, we need the city and state data as well. So we will create a new variable in our data called address using the mutate() function from the dplyr package.

This variable will collapse the values in the School, City, and State columns but with spaces in between. It is specified such that there will be space in between by the sep = " " argument.

Note: a space is typed between the quotation marks.

In this way, we then can use the address variable to look up the latitude and longitude for each school.

shooting_data %<>%
  dplyr::mutate(address = 
                  stringr::str_c(School, City, State_abb, sep = " "))

We can take a look at just this new address variable using the pull() function from the dplyr() package.

shooting_data %>%
  dplyr::pull(address) %>% 
  head()
[1] "Hine Junior High School Washington DC"       
[2] "Sousa Junior High Washington DC"             
[3] "Unnamed High School Washington DC"           
[4] "John F. Kennedy High School Cleveland OH"    
[5] "David Starr Jordan High School Long Beach CA"
[6] "Pine Bluff Coleman High School Pine Bluff AR"

Now we can use these addresses to find the latitude and longitude coordinates for each school where a school shooting occurred.

To do this, we will use the geocode() function from the ggmap package to look up these addresses on Google Maps to get the latitude and longitude values. In the geocode() function, we also need to specify that we want to use google as the source using the source argument and that we want latitude and longitude using the output = c("latlon") argument.

This step requires registering with the Google Cloud Platform to get an API key, which currently requires registering your payment information and agreeing to the Google Maps API Terms of Service.

You are not required to do this yourself! We will give you the data.

Click here to see how this process works in general.
Click here to see how we registered with the Google Cloud Platform.

If you were to do this process yourself, you could get an API key here. Again this requires registering your payment information, but it is free to got an API key and enable the APIs, however you can be billed based on how many addresses you look up using the APIs. You need to look up thousands before getting billed.

Then you need to enable the maps and places APIs, by clicking on the boxes next to each:

Then you would register like so after copying the API key: (Note this is a fake key)

ggmap::register_google(key = "mQkzTpiaLYjPqXQBotesgif3EfGL2dbrNVOrogg") 

Once we have obtained an API key and are registered, we can geocode our data.

Note that this step is time intensive, as there are many addresses to look up! Therefore, we will just show how this is done and will not evaluate the code for the next few code chunks. Again we will use the geocode() function from the ggmap package to perform this step.

shooting_data <- 
  shooting_data %>%
  mutate(coords = ggmap::geocode(address, 
                                 output = c("latlon"), 
                                 source = c("google")))

This results in tibble called coords being added to our shooting_data tibble. That’s right, we can have a tibble as a column or variable within a tibble. Using the glimpse function again, and looking at the last few variables, we can see that now the last variable listed is coords of class <tibble>.

If we take a look at the first couple of values of the coords tibble, we see a tibble that looks like this:

It would be better if each of these were their own columns in the tibble, so we will create new longitude and latitude variables again using the mutate function like so:

shooting_data <- 
  shooting_data %>%
  mutate(longitude = pull(coords,lon),
         latitude = pull(coords,lat))

In this case we use the pull() function to grab the lat and lon variables within the coords tibble which is a variable of the shooting_data tibble. This can also be done using the unpack() function from the tidyr package.

We can now remove the coords tibble like so, using the select() function from the dplyr package:

shooting_data <- 
  shooting_data %>%
  dplyr::select(-coords)

Now using glimpse() and looking at the last several variables, we can see that we no longer have a coords variable, but we do have two variables called longitude and latitude that are of class double as indicated by the <dbl>:

Now we will save the geocoded data in the wrangled directory of our data directory using the save function.

This requires listing the R object, followed by the path for where the file should be saved and what it should be called. In this case it will be called shooting_data_wrangled_pre_map.rda. First let’s create a new object called shooting_data_wrangled_pre_map so it is clear in the future what we are working with when we load the data. We will also write this data to a csv file, which can be convenient for collaborators. To do this we will use the write_csv() function from the readr package.

shooting_data_wrangled_pre_map <- shooting_data
save(shooting_data_wrangled_pre_map, file = here("data", "wrangled",
                             "shooting_data_wrangled_pre_map.rda"))

readr::write_csv(shooting_data_wrangled_pre_map, 
                 file = here("data", "wrangled",
                             "shooting_data_wrangled_pre_map.csv"))

You can download the wrangled data with latitude and longitude values using the OCSdata package:

# library(OCSdata)
wrangled_rda("ocs-bp-school-shootings-dashboard", outpath = getwd())
# load(file = here("OCSdata", "data", "wrangled", "shooting_data_wrangled_pre_map.rda"))

You can also access this data here.

To load the data, you may double click the downloaded .rda file in RStudio, or put the downloaded file in the appropriate directory and use the following command.

load(file = here("data", "wrangled",
                             "shooting_data_wrangled_pre_map.rda"))

Geometry lists with the sf package

In this section, we will use the sf (which stands for simple features) package to create what is called a geometry list of our latitude and longitude information for the schools where shootings occurred. As some school shootings occurred in the same location, we need to alter all of the locations a bit so that when we plot the data on a map, the spots indicating where school shootings occurred will not overlap for the same location.

Let’s learn how to do this.

shooting_data_wrangled_pre_map %>% 
  select(latitude, longitude) %>% 
  slice_head(n = 5)
# A tibble: 5 x 2
  latitude longitude
     <dbl>     <dbl>
1     38.9     -77.0
2     38.9     -77.0
3     38.9     -77.0
4     41.4     -81.6
5     33.9    -118. 

First, let’s remind ourselves how many rows we have in our dataset.

dim(shooting_data_wrangled_pre_map)
[1] 1556   52

Since tibbles give dimensions, instead of using the function dim(), we might also check the dimensions of our dataset by simply doing so:

shooting_data_wrangled_pre_map
# A tibble: 1,556 x 52
   Date       School   City  State_abb `Reliability S~` `Killed (inclu~` Wounded
   <date>     <chr>    <chr> <chr>                <dbl>            <dbl>   <dbl>
 1 1970-01-05 Hine Ju~ Wash~ DC                       3                1       0
 2 1970-01-05 Sousa J~ Wash~ DC                       3                0       1
 3 1970-01-05 Unnamed~ Wash~ DC                       2                0       0
 4 1970-02-06 John F.~ Clev~ OH                       2                0       1
 5 1970-03-23 David S~ Long~ CA                       2                0       2
 6 1970-04-15 Pine Bl~ Pine~ AR                       3                1       5
 7 1970-04-22 Pierre ~ Wilm~ DE                       3                1       0
 8 1970-05-08 Carver ~ Delr~ FL                       2                0       1
 9 1970-05-15 Ben Lom~ Ogden UT                       2                0       2
10 1970-08-28 Riversi~ El P~ TX                       2                0       1
# ... with 1,546 more rows, and 45 more variables:
#   `Total Injured/Killed Victims` <dbl>, `Gender of Victims (M/F/Both)` <chr>,
#   `Victim's Affiliation w/ School` <chr>, `Victim's age(s)` <dbl>,
#   `Victims Race` <chr>, `Victim Ethnicity` <chr>,
#   `Targeted Specific Victim(s)` <lgl>, `Random Victims` <lgl>,
#   `Bullied (Y/N/ N/A)` <lgl>, `Domestic Violence (Y/N)` <lgl>,
#   `Suicide (Shooter was only victim) Y/N/ N/A` <lgl>, ...

This is important because not all rows have a recorded latitude and longitude.

shooting_data_wrangled_pre_map %>% 
  filter(is.na(latitude)) %>% 
  select(longitude, latitude, address)
# A tibble: 5 x 3
  longitude latitude address                                    
      <dbl>    <dbl> <chr>                                      
1        NA       NA John Marshall High School Los Angeles CA   
2        NA       NA John Marshall High School Los Angeles CA   
3        NA       NA John Marshall High School Los Angeles CA   
4        NA       NA Buell Elementary School Flint MI           
5        NA       NA Country Day High School Estate Concordia VI

Therefore, before we can proceed, we need to remove rows with NA values for the latitude and longitude variables. In other words, we need to remove rows of events that happened at schools with locations that were not identified by Google.

We can remove these rows using the drop_na() function from the tidyr package. We will use a . to indicate that we want to use the data that we are using as an input with our pipe, but then we will specify that we want to only drop rows were there is an NA value for either the latitude or longitude variables.

shooting_data_wrangled_for_map <- shooting_data_wrangled_pre_map %>%
 tidyr::drop_na(c(latitude, longitude))

How many did we remove? Let’s look at the dimension of our new dataset.

dim(shooting_data_wrangled_for_map)
[1] 1551   52

We see that there were 5 events that occurred at schools with unidentified complete locations (missing either latitude, longitude, or both) that were removed from our dataset.

Next, we are ready to convert our coordinates variables (latitude and longitude) into a coordinate simple feature (or sf object) using the st_as_sf() function (converts foreign object to an sf object).

To do this, we need to specify what our coordinate variables are and we will also specify what coordinate reference system,(crs) we would like to use. In our case we will use the ESPG reference number 4326, known as ESPG:4326 or the World Geodetic System (WGS) version 84 which is one of the most commonly used CPS and used by by most global positioning systems, known as GPS. This tells R to use the values for the variables called latitude and longitude as latitude and longitude coordinates.

shooting_data_wrangled_for_map %<>%
  sf::st_as_sf(coords = c("longitude", "latitude"), crs = 4326)

dim(shooting_data_wrangled_pre_map)
[1] 1556   52

We can see that our latitude and longitude variables were used to create a single new variable called geometry of class <POINT [\(^{\circ}\)]>, thus we have one less column.

In this case, this type of variable will always be shown. Even if we were to look at just the first 4 variables using indexes (like this: [1:4]), we will also see our last sf variable appended at the end.

So now we can see all variables related to location (which happen to be the first four variables and the geometry variable) by simply typing [1:4] next to the name of our tibble shooting_data_geocoded.

shooting_data_wrangled_for_map[1:4]
Simple feature collection with 1551 features and 4 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -161.7705 ymin: 21.31061 xmax: -68.85822 ymax: 61.23202
Geodetic CRS:  WGS 84
# A tibble: 1,551 x 5
   Date       School                   City  State_abb             geometry
   <date>     <chr>                    <chr> <chr>              <POINT [°]>
 1 1970-01-05 Hine Junior High School  Wash~ DC        (-76.97829 38.89261)
 2 1970-01-05 Sousa Junior High        Wash~ DC        (-76.95315 38.88397)
 3 1970-01-05 Unnamed High School      Wash~ DC        (-76.98263 38.86993)
 4 1970-02-06 John F. Kennedy High Sc~ Clev~ OH         (-81.57341 41.4425)
 5 1970-03-23 David Starr Jordan High~ Long~ CA        (-118.1842 33.87129)
 6 1970-04-15 Pine Bluff Coleman High~ Pine~ AR        (-92.05407 34.21608)
 7 1970-04-22 Pierre S. Dupont High S~ Wilm~ DE        (-75.53298 39.76428)
 8 1970-05-08 Carver High School       Delr~ FL        (-80.11266 26.45975)
 9 1970-05-15 Ben Lomond High School   Ogden UT         (-111.951 41.25111)
10 1970-08-28 Riverside High School    El P~ TX        (-106.3723 31.73379)
# ... with 1,541 more rows

To allow our points to not overlap for events that took place in the same location, we will add a bit more range so that they do not overlap one another on our map.

To do this, we will transform the coordinates using the st_transform() function of the sf package into a two dimensional projection (called the Albers equal-area conic projection) with units in meters using the crs 102008 reference from the Environmental Systems Research Institute (ERSI) and then use the st_jitter() function from the sf package to allow a specified amount of range near the actual original GPS coordinates.

To learn more about geospatial coordinate systems see here and here.

So here we can see the output after transforming our data to the crs 102008 reference:

shooting_data_wrangled_for_map  %<>%
  st_transform(crs = "ESRI:102008") 

Notice how the class for the geometry variable is now <POINT [m]> as our data has been transformed into coordinates in meters.

shooting_data_wrangled_pre_map[1:5]
# A tibble: 1,556 x 5
   Date       School                         City     State_abb `Reliability S~`
   <date>     <chr>                          <chr>    <chr>                <dbl>
 1 1970-01-05 Hine Junior High School        Washing~ DC                       3
 2 1970-01-05 Sousa Junior High              Washing~ DC                       3
 3 1970-01-05 Unnamed High School            Washing~ DC                       2
 4 1970-02-06 John F. Kennedy High School    Clevela~ OH                       2
 5 1970-03-23 David Starr Jordan High School Long Be~ CA                       2
 6 1970-04-15 Pine Bluff Coleman High School Pine Bl~ AR                       3
 7 1970-04-22 Pierre S. Dupont High School   Wilming~ DE                       3
 8 1970-05-08 Carver High School             Delray ~ FL                       2
 9 1970-05-15 Ben Lomond High School         Ogden    UT                       2
10 1970-08-28 Riverside High School          El Paso  TX                       2
# ... with 1,546 more rows

And now we will add a jitter to the points using the st_jitter() function, meaning that we will randomly move the coordinates a little bit to allow for points at the same location to not overlap on the map.

You can see the tidyverse explanation about when to use a jitter plot here, they state that a jitter:

adds a small amount of random variation to the location of each point, and is a useful way of handling overplotting caused by discreteness in smaller datasets.

In this case we will allow for 50 meters of range using the amount = 50 argument.

shooting_data_wrangled_for_map %<>%
   sf::st_jitter(amount = 50)

We can now see that the coordinates are slightly modified.

shooting_data_wrangled_for_map[1:4]
Simple feature collection with 1551 features and 4 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -5937651 ymin: -1635649 xmax: 2001949 ymax: 3621440
Projected CRS: North_America_Albers_Equal_Area_Conic
# A tibble: 1,551 x 5
   Date       School                   City  State_abb            geometry
   <date>     <chr>                    <chr> <chr>             <POINT [m]>
 1 1970-01-05 Hine Junior High School  Wash~ DC         (1544769 25000.97)
 2 1970-01-05 Sousa Junior High        Wash~ DC         (1546940 24330.63)
 3 1970-01-05 Unnamed High School      Wash~ DC         (1544914 22217.39)
 4 1970-02-06 John F. Kennedy High Sc~ Clev~ OH         (1129302 256394.4)
 5 1970-03-23 David Starr Jordan High~ Long~ CA         (-1933757 -492216)
 6 1970-04-15 Pine Bluff Coleman High~ Pine~ AR         (345323 -672074.4)
 7 1970-04-22 Pierre S. Dupont High S~ Wilm~ DE         (1638335 149770.5)
 8 1970-05-08 Carver High School       Delr~ FL         (1533744 -1445853)
 9 1970-05-15 Ben Lomond High School   Ogden UT        (-1251326 253167.8)
10 1970-08-28 Riverside High School    El P~ TX        (-937745.4 -916826)
# ... with 1,541 more rows

Note: the geometry values have changed.

Now we will transform our coordinates back into the 3D latitude and longitude degree system again using the st_transform() function and the ESPG:4326, coordinate system.

shooting_data_wrangled_for_map  %<>%
  st_transform(crs = 4326)

shooting_data_wrangled_for_map[1:4]
Simple feature collection with 1551 features and 4 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -161.7708 ymin: 21.31092 xmax: -68.85789 ymax: 61.23175
Geodetic CRS:  WGS 84
# A tibble: 1,551 x 5
   Date       School                   City  State_abb             geometry
   <date>     <chr>                    <chr> <chr>              <POINT [°]>
 1 1970-01-05 Hine Junior High School  Wash~ DC        (-76.97776 38.89293)
 2 1970-01-05 Sousa Junior High        Wash~ DC        (-76.95337 38.88368)
 3 1970-01-05 Unnamed High School      Wash~ DC        (-76.98281 38.86953)
 4 1970-02-06 John F. Kennedy High Sc~ Clev~ OH        (-81.57356 41.44271)
 5 1970-03-23 David Starr Jordan High~ Long~ CA        (-118.1841 33.87105)
 6 1970-04-15 Pine Bluff Coleman High~ Pine~ AR        (-92.05433 34.21595)
 7 1970-04-22 Pierre S. Dupont High S~ Wilm~ DE        (-75.53289 39.76448)
 8 1970-05-08 Carver High School       Delr~ FL        (-80.11234 26.45968)
 9 1970-05-15 Ben Lomond High School   Ogden UT         (-111.9505 41.2509)
10 1970-08-28 Riverside High School    El P~ TX         (-106.3719 31.7336)
# ... with 1,541 more rows

Notice how the geometry variables are different from what they were originally with this coordinate system:

Next, we separate the geometry variable into longitude and latitude variables again. We can use the st_coordinates() function from the sf package to extract the coordinates from our tibble as a matrix.

shooting_data_wrangled_for_map %<>% 
  mutate(coordinates = as_tibble(st_coordinates(.)))

shooting_data_wrangled_for_map %>%
  pull(coordinates) %>%
  slice_head(n = 4)
# A tibble: 4 x 2
      X     Y
  <dbl> <dbl>
1 -77.0  38.9
2 -77.0  38.9
3 -77.0  38.9
4 -81.6  41.4

Now, just as we did previously we will create new variables called latitude and longitude from the X and Y variables within the coordinates tibble that is part of our shooting_data_wrangled_for_map using the pull() function.

We will also convert our shooting_data_wrangled_for_map object which is currently a sf into a tibble using the as_tibble() function from the tibble package and then we will remove the geometry and coordinates variables using the select() function from the dplyr package with a minus operator in front of the names of the variables to remove.

shooting_data_wrangled_for_map %<>%
  mutate(longitude = pull(coordinates,X),
          latitude = pull(coordinates,Y)) %>%
  tibble::as_tibble() %>%
  select(-geometry) %>%
  select(-coordinates)

And now we can take a look at our last 3 variables using the last_col() function, which is a select() helper function tidyr package (See here for other select() helper functions).

The last_col() function allows us to select either the last column, or with a specified offset we can select a number of columns before the last column. Thus, 2 columns before the last column would be last_col(offset = 2) and then the : symbol is interpreted as through, thus we are selecting for the third to last column through the last column with last_col(offset = 2):last_col().

shooting_data_wrangled_for_map %>% 
  select(tidyr::last_col(offset = 2):last_col()) %>% 
  slice_head(n = 4)
# A tibble: 4 x 3
  address                                  longitude latitude
  <chr>                                        <dbl>    <dbl>
1 Hine Junior High School Washington DC        -77.0     38.9
2 Sousa Junior High Washington DC              -77.0     38.9
3 Unnamed High School Washington DC            -77.0     38.9
4 John F. Kennedy High School Cleveland OH     -81.6     41.4

Great! That looks like we expected.

Finally, we will save our wrangled data, again using save() and we will also write to a CSV file using write_csv().

save(shooting_data_wrangled_for_map, 
          file = here("data", "wrangled",
                      "shooting_data_wrangled_for_map.rda"))

write_csv(shooting_data_wrangled_for_map, 
          file = here("data", "wrangled",
                      "shooting_data_wrangled_for_map.csv"))

Data Analysis and Visualization


If you have been following along but stopped, we could load our data like so:

load(here::here("data", "wrangled", "shooting_data_wrangled_pre_map.rda"))
load(here::here("data", "wrangled", "shooting_data_wrangled_for_map.rda"))

We need the wangled data that is prepared both for the map and the data just before the last wrangling step Geometry lists with the sf package] to prepare for the map because we removed some events that did not have addresses that were identified by Google (had NA values for latitude or longitude). We want to use data for all events for our statistics, tables, and plots.

If you skipped the previous sections click here.

First you need to install the OCSdata package:

install.packages("OCSdata")

Then, you may download the wrangled data .rda files like so:

# library(OCSdata)
wrangled_rda("ocs-bp-school-shootings-dashboard", outpath = getwd())
# load(here::here("OCSdata", "data", "wrangled", "shooting_data_wrangled_pre_map.rda"))
# load(here::here("OCSdata", "data", "wrangled", "shooting_data_wrangled_for_map.rda"))

To load the downloaded data into your environment, you may double click on each of the .rda files in Rstudio or using the load() function.

If the package does not work for you, two RDA files (stands for R data) of the data can be found here or slightly more directly here and here. Download these files and then place them in your current working directory. We recommend using an RStudio project and the here package to navigate to your files more easily.

We have put these files in a directory called “wrangled” within a directory called “data” within our working directory (which has a .Rproj file).

load(here::here("data", "wrangled", "shooting_data_wrangled_pre_map.rda"))
load(here::here("data", "wrangled", "shooting_data_wrangled_for_map.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects.


Luckily, our data is already in pretty good shape, but we want to make our data more useful for our dashboard.

Let’s shorten the name of the data that was wrangled up to the last step for the map. We will use shooting_data.

shooting_data <-shooting_data_wrangled_pre_map

We will also rename the data that is wrangled for the map to a shorter name:

shooting_data_for_map <- shooting_data_wrangled_for_map

Let’s double check that our data is expected:

dim(shooting_data)
[1] 1556   52
dim(shooting_data_for_map)
[1] 1551   52

Great, looks like we indeed have more rows in our shooting_data as we would expect.

There are several elements we would like to include in our dashboard.

One thing we would like is an interactive table.

Interactive Table


We can do this using the datatable() function from the DT package.

DT::datatable(shooting_data)

This creates a searchable table and the order in which the data is displayed can be toggled to change for each variable.

However, we have many variables or columns in our dataset, so this can be overwhelming. Instead of displaying all of the variables, let’s choose only some of the most interesting to display in our dashboard.

DT_table <- 
  shooting_data %>%
  dplyr::select(Date,
                School,
                City,
                State,
                `Killed (includes shooter)`,
                `Narrative (Detailed Summary/ Background)`) %>%
  rename("Deaths" = `Killed (includes shooter)`) %>%
  rename("Narrative" = `Narrative (Detailed Summary/ Background)`)

DT::datatable(DT_table)

Next, we will make some data visualizations.

Yearly Shootings


We would like to create a plot of the number of school shootings per year.

To do this, we will count the number of school shootings per year using the count() function from the dplyr package. We specify that we want to count the unique values of the Date_year variable and name the new column Shootings.

shootings_per_year <-
  shooting_data %>%
  count(Date_year, name = "Shootings")

shootings_per_year
# A tibble: 51 x 2
   Date_year Shootings
       <dbl>     <int>
 1      1970        20
 2      1971        21
 3      1972        18
 4      1973        18
 5      1974        16
 6      1975        14
 7      1976        11
 8      1977        16
 9      1978        16
10      1979        14
# ... with 41 more rows

Good, this looks as expected.

Now to make a plot of this data we will use the ggplot2 package.

Click here for an introduction to ggplot2.

The ggplot2 package is generally intuitive for beginners because it is based on a grammar of graphics or the gg in ggplot2. The idea is that you can construct many sentences by learning just a few nouns, adjectives, and verbs. There are specific “words” that we will need to learn and once we do, you will be able to create (or “write”) hundreds of different plots.

The critical part to making graphics using ggplot2 is the data needs to be in a tidy format. Given that we have just spent time putting our data in tidy format, we are primed to take advantage of all that ggplot2 has to offer!

We will show how it is easy to pipe tidy data (output) as input to other functions that create plots. This all works because we are working within the tidyverse.

What is the ggplot() function? As explained by Hadley Wickham:

The grammar tells us that a statistical graphic is a mapping from data to aesthetic attributes (colour, shape, size) of geometric objects (points, lines, bars). The plot may also contain statistical transformations of the data and is drawn on a specific coordinates system.

ggplot2 Terminology:

  • ggplot - the main function where you specify the dataset and variables to plot (this is where we define the x and y variable names)
  • geoms - geometric objects
    • e.g. geom_point(), geom_bar(), geom_line(), geom_histogram()
  • aes - aesthetics
    • shape, transparency, color, fill, line types
  • scales - define how your data will be plotted
    • continuous, discrete, log, etc

The function aes() is an aesthetic mapping function inside the ggplot() object. We use this function to specify plot attributes (e.g. x and y variable names) that will not change as we add more layers.

Anything that goes in the ggplot() object becomes a global setting. From there, we use the geom objects to add more layers to the base ggplot() object. These will define what we are interested in illustrating using the data.


For more of an introduction on creating plots with ggplot2 , see this case study


First, we start with the ggplot() function from the ggplot2 package.

This function requires that the aesthetics aes() be specified. This involves choosing what variable will be plotted on the x-axis and the y-axis.

shootings_per_year %>%
    ggplot(aes(x = Date_year, y = Shootings))

Using the ggplot() function alone will create an empty plot area. To make our plot not empty, we need to select one of the geom_* functions of the ggplot2 package to specify what type of plot we want to create.

Assuming the ggplot2 library is loaded, type geom into the RStudio console and you will see many options to scroll through.

Here, we use a geom_col() plot, which is a particular type of bar plot that uses the actual values to plot, rather than counts, which is the default of geom_bar(). We will specify with the fill argument, that we want our bars to be filled with the color black.

shootings_per_year %>%
    ggplot(aes(x = Date_year, y = Shootings)) +
    geom_col(fill = "black")

We also modify the x-axis using the scale_x_continuous() function. This function allows for specification of the range or limits of the axis using the limits argument. We can use the base seq() function to create a sequence of numbers for each tick mark.

We can add labels to our plot using the labs() function from ggplot2. This has arguments such as x and y for the axes and title and subtitle for titles. We can use NULL to remove a label. For example to remove the x-axis label we can use x = NULL

We will also modify the overall aesthetics of the plot using a theme_* function. See here for a list of options.

start <- 1970
end <- 2020

shootings_per_year_p <- 
  shootings_per_year %>%
    ggplot(aes(x = Date_year, y = Shootings)) +
      geom_col(fill = "black") +
      scale_x_continuous(breaks = seq(start, end, by = 5),
                         labels = seq(start, end, by = 5),
                         limits = c(start-1, end+1)) +
      theme_minimal() +
      labs(title = "Yearly School Shootings",
           subtitle = "United States",
           x = NULL,
           y = "School Shootings")

shootings_per_year_p 

Yearly Deaths


Let’s make a similar plot for the number of deaths

deaths_per_year<-
  shooting_data %>% 
  group_by(Date_year) %>%
  summarize(Deaths = sum(`Killed (includes shooter)`))

deaths_per_year_p <- 
  deaths_per_year %>%
    ggplot(aes(x = Date_year, y = Deaths)) +
      geom_col(fill = "black") +
      scale_x_continuous(breaks = seq(start, end, by = 5),
                         labels = seq(start, end, by = 5),
                         limits = c(start-1, end+1)) +
      theme_minimal() +
      labs(title = "Yearly Deaths Attributable to School Shootings",
           subtitle = "United States",
           x = NULL)

deaths_per_year_p

Note: When using the summarize() function, we don’t need to use the mutate() function here.

Next, for the purposes of the dashboard, we actually want to create just one plot that shows both the number of school shootings per year and the number of deaths.

We can do so by combining our shootings_per_year and deaths_per_year tibbles together and making what is called a faceted plot, using the facet_wrap() function to create two plots next to one another.

To combine our data we will use the full_join() function from the dplyr package. This maintains all values from both tibbles.

To do so we will be making our table “longer”, meaning that it will have fewer columns and more rows. See here for more information about different table formats, typically referred to as wide and long or sometimes narrow.

We will use the pivot_longer() function from the tidyr package to change the shape of our table.

There are 3 main arguments in this function:

  1. cols - which specifies what columns to collapse
  2. names_to - which specifies the name of the new column that will be created that will contain the column names of the columns you are collapsing
  3. values_to - which specifies the name of the new column that will be created that will contain the values from the columns you are collapsing

To specify that we want to collapse all the columns that have year values, we can choose all those except the Date_year variable by using the - negative operator.

per_year <- 
  full_join(shootings_per_year, deaths_per_year)

per_year %<>%
  pivot_longer(cols = -Date_year, 
               values_to = "events", 
               names_to = "id")

per_year
# A tibble: 102 x 3
   Date_year id        events
       <dbl> <chr>      <dbl>
 1      1970 Shootings     20
 2      1970 Deaths         8
 3      1971 Shootings     21
 4      1971 Deaths         9
 5      1972 Shootings     18
 6      1972 Deaths         6
 7      1973 Shootings     18
 8      1973 Deaths         6
 9      1974 Shootings     16
10      1974 Deaths        12
# ... with 92 more rows

Hmmm, we see the data type of the id column is a character (<chr>). Let’s convert it to a factor, so that the order in which Shootings and Deaths appear is the order in which they appear first rather than by alphabetical order (which is default).

Using the fct_inorder() function from the forcats package, we can easily reorder the id variable`.

per_year %<>% 
  mutate(id = forcats::fct_inorder(id))

per_year
# A tibble: 102 x 3
   Date_year id        events
       <dbl> <fct>      <dbl>
 1      1970 Shootings     20
 2      1970 Deaths         8
 3      1971 Shootings     21
 4      1971 Deaths         9
 5      1972 Shootings     18
 6      1972 Deaths         6
 7      1973 Shootings     18
 8      1973 Deaths         6
 9      1974 Shootings     16
10      1974 Deaths        12
# ... with 92 more rows

Now since we the new variable for the names is called id we will use this as the variable to create the facet like so: facet_wrap(~id). We can also specify that we want both plots to have their own y-axis with the scales = "free" argument. This causes each to have the y-axis automatically scaled for the data in each plot. We can then use the scale_y_continuous() function to set both of the y-axes to be the same.

per_year %>%
  ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col() +
    facet_wrap(~id, scales = "free") +
    scale_x_continuous(breaks = seq(start, end, by = 5),
                       labels = seq(start, end, by = 5),
                       limits = c(start-1, end+1)) +
    scale_y_continuous(breaks = seq(0, 120, by = 30),
                       labels = seq(0, 120, by = 30),
                       limits = c(0, 121))+
    theme_minimal() +
    labs(title = "Yearly Shootings and Deaths Attributable to School Shootings",
         subtitle = "United States",
         y = "Number of events",
         x = "Year")+
    scale_fill_manual(values = c("black", "black"))+
    theme(legend.position = "none", 
          legend.title = element_blank(),
          axis.text.x = element_text(angle = 90),
          strip.background =element_rect(fill="cornflowerblue"),
          strip.text = element_text(colour = 'white', face = "bold", size = 14))

Next, we can modify the plot further so that it is more obvious what each plot is showing. We can update the names of the y-axis for each plot by changing the strip.position argument of the facet_wrap() function to be placed on the left rather than above. Currently it is the label in blue that says what the value of the id variable is for each plot. This also requires some modification of the theme() function to place the strip.text outside the plot area and to remove the background.Furthermore, we also change the text using the labeller argument of the facet_wrap() function. The as_labeller() function from the ggplot2 package can change out the id values for other text like in the following code:

per_year %>%
  ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col() +
    facet_wrap(~id, 
               scales = "free", 
               labeller = as_labeller(c(Shootings = "Shootings (# of events)", 
                                        Deaths = "Deaths (# of people)")), 
               strip.position = "left") +
    scale_x_continuous(breaks = seq(start, end, by = 5),
                       labels = seq(start, end, by = 5),
                       limits = c(start-1, end+1)) +
    scale_y_continuous(breaks = seq(0, 120, by = 30),
                       labels = seq(0, 120, by = 30),
                       limits = c(0, 121))+
    theme_minimal() +
    labs(title = "Yearly Shootings and Deaths Attributable to School Shootings",
         subtitle = "United States",
         y = NULL,
         x = "Year")+
    scale_fill_manual(values = c("black", "black"))+
    theme(legend.position = "none", 
          legend.title = element_blank(),
          axis.text.x = element_text(angle = 90),
          strip.background = element_blank(),
          strip.placement = "outside",
          strip.text = element_text(face = "bold", size = 14))

Good, Now this is much easier to interpret.

Our last step in this section is to save the style settings of this plot as theme so we can reuse it for future plots. To do this, we use the base function() function:

theme_dashboard <- function(){ 
  theme(legend.position = "none", 
        legend.title = element_blank(),
        axis.text.x = element_text(angle = 90, face = "bold"),
        axis.title.x = element_text(face = "bold", size = 14),
        strip.background = element_blank(),
        strip.placement = "outside",
        strip.text = element_text(face = "bold", size = 14))
}

Yearly Cumulative Shootings


Now let’s make another plot of the cumulative deaths each year including those of the previous years. In this case we can use the shootings_per_year object that we previously made.

shootings_per_year
# A tibble: 51 x 2
   Date_year Shootings
       <dbl>     <int>
 1      1970        20
 2      1971        21
 3      1972        18
 4      1973        18
 5      1974        16
 6      1975        14
 7      1976        11
 8      1977        16
 9      1978        16
10      1979        14
# ... with 41 more rows

However, we want to add a new variable using the mutate function called n_cum_sum by using the cumsum() function to calculate a cumulative sum based on the yearly count.

shootings_per_year_cum <- shootings_per_year %>%
    mutate(Shootings = cumsum(Shootings))

deaths_per_year_cum <- deaths_per_year %>%
    mutate(Deaths = cumsum(Deaths))

shootings_per_year_cum
# A tibble: 51 x 2
   Date_year Shootings
       <dbl>     <int>
 1      1970        20
 2      1971        41
 3      1972        59
 4      1973        77
 5      1974        93
 6      1975       107
 7      1976       118
 8      1977       134
 9      1978       150
10      1979       164
# ... with 41 more rows

Next, we join these tables together

per_year_cum <- 
  full_join(shootings_per_year_cum, deaths_per_year_cum)

per_year_cum %<>% 
  pivot_longer(cols = c(Shootings,Deaths ), 
               values_to = "events", 
               names_to = "id")

per_year_cum
# A tibble: 102 x 3
   Date_year id        events
       <dbl> <chr>      <dbl>
 1      1970 Shootings     20
 2      1970 Deaths         8
 3      1971 Shootings     41
 4      1971 Deaths        17
 5      1972 Shootings     59
 6      1972 Deaths        23
 7      1973 Shootings     77
 8      1973 Deaths        29
 9      1974 Shootings     93
10      1974 Deaths        41
# ... with 92 more rows

Good, this looks like we would expect.

Now let’s make a plot like we did before:

per_year_cum %<>% 
  mutate(id = forcats::fct_inorder(id))

per_year_cum %>%
    ggplot(aes(x = Date_year, y = events, fill = id)) +
      geom_col() +
      facet_wrap(~id, scales = "free", 
                 labeller = as_labeller(c(Shootings = "Shootings (# of events)", 
                                          Deaths = "Deaths (# of people)")), 
                 strip.position = "left") +
      scale_x_continuous(breaks = seq(start, end, by = 5),
                         labels = seq(start, end, by = 5),
                         limits = c(start-1, end+1)) +
      scale_y_continuous(breaks = seq(0, 1500, by = 500),
                         labels = seq(0, 1500, by = 500),
                         limits = c(0, 1500)) +
      theme_minimal() +
      labs(title = "Cumulative Yearly Shootings and Deaths\nAttributable to School Shootings",
           subtitle = "United States",
           y = NULL,
           x = "Year") +
      scale_fill_manual(values = c("black", "black")) +
      theme_dashboard()

Note: the limits for the y-axis were determined by first plotting without the scale_y_continuous() function.

Deaths per Shooting


Next, we will make a plot of the number of deaths per school shooting based on the Killed (includes shooter) variable.

Question Opportunity

See if you can come up with the code for the plot.


Click here to reveal the answer.
deaths_per_event <-
  shooting_data %>%
  group_by(`Killed (includes shooter)`) %>%
  count() %>%
  ungroup()

per_shooting_plot <-deaths_per_event %>%
  ggplot(aes(y = `Killed (includes shooter)`, x = n)) +
    geom_col(fill = "black")+
    theme_minimal() +
    labs(title = "Deaths per School Shooting",
         subtitle = "United States",
         x = "School Shootings",
         y = "")

per_shooting_plot 

This plot could also have been made using geom_bar() instead of geom_col() this makes a similar plot but automatically uses the count for one of the axes, thus it is not required to first summarize the data using the count() function.

shooting_data %>%
  ggplot(aes(x = `Killed (includes shooter)`)) +
    geom_bar(fill = "black") +
    theme_minimal() +
    labs(title = "Deaths per School Shooting",
         subtitle = "United States",
         x = "School Shootings",
         y = "")

Because of the skewed distribution, it is difficult to see the school shootings that had more numerous deaths, so we will add a facet that zooms in on this portion of the plot. We can do so, using the facet_zoom() function from the ggforce package.

shooting_data %>%
  ggplot(aes(x = `Killed (includes shooter)`)) +
    geom_bar(fill = "black") +
    scale_x_continuous(breaks = seq(0, max(pull(shooting_data, 
                                    `Killed (includes shooter)`)), by = 1),
                       labels = seq(0, max(pull(shooting_data,
                                    `Killed (includes shooter)`)), by = 1)) +
    ggforce::facet_zoom(xlim = c(4, max(pull(shooting_data, 
                                    `Killed (includes shooter)`))), 
                        ylim = c(0,20)) +
    theme_minimal() +
    labs(title = "Deaths per School Shooting",
         subtitle = "United States",
         x = "Deaths per shooting",
         y = "Number of events with given number of deaths") +
  theme(axis.text.x = element_text(angle = 90))

It is still difficult to see. Let’s try some other options.

The geom_freqpoly() function creates a graph that makes it very easy to see that most school shootings result in zero or one death and that the maximum number of deaths in this data for a single event is in the upper twenties.

shooting_data %>%
    ggplot(aes(x = `Killed (includes shooter)`)) +
    geom_freqpoly()

This really shows that most school shooting events luckily result in no deaths, but what are the actual proportions of school shootings that end in 0 deaths, 1 death, 2 deaths, etc. One way to look at this is to calculate the percentage of events that resulted in each number of deaths. We can do this by dividing the number of events by the overall sum of events and multiplying by 100. The base round() function can round this value to the nearest 1 decimal place by specifying that we want 1 digit after the decimal with digits = 1.

deaths_perc_event <-
  shooting_data %>%
  count(`Killed (includes shooter)`) %>%
  rename("num_events"= n) %>%
  mutate(percent = round(num_events/sum(num_events)*100, digits =1))

deaths_perc_event
# A tibble: 11 x 3
   `Killed (includes shooter)` num_events percent
                         <dbl>      <int>   <dbl>
 1                           0        969    62.3
 2                           1        505    32.5
 3                           2         58     3.7
 4                           3         12     0.8
 5                           4          1     0.1
 6                           5          3     0.2
 7                           6          3     0.2
 8                          10          2     0.1
 9                          15          1     0.1
10                          17          1     0.1
11                          28          1     0.1
deaths_perc_event %>%
  ggplot(aes(x =`Killed (includes shooter)`, y = percent)) +
    geom_col()

We can see that greater than 60% of the events had no deaths. It is however, the plot is still unsatisfactory because there is such a long tail.

Next, we can try collapsing the events that resulted in 4 or more deaths together and create a pie chart which you are likely familiar with as well as alternative plot called a waffle plot.

First to collapse the percentage for the events that had 4 or more deaths, we need to do a bit of wrangling.

We will start with filtering the data to only these events and then we will sum each of the columns using the base R function colSums() with the goal of creating a new row in the deaths_perc_event object that will contain information about all events with 4 or more deaths. We will use the >= greater than or equal to operator.

greater_than4 <- 
  deaths_perc_event %>% 
  filter(`Killed (includes shooter)` >= 4) %>% 
  colSums()

greater_than4
Killed (includes shooter)                num_events                   percent 
                     85.0                      12.0                       0.9 

Good, now we know the overall percentage for the events that unfortunately resulted in more than 4 deaths.

Next, we combine this with the rest of our data using the bind_rows() function from the dplyr package which appends a tibble to another.

[source]
deaths_perc_event %<>%
  bind_rows(greater_than4)

deaths_perc_event 
# A tibble: 12 x 3
   `Killed (includes shooter)` num_events percent
                         <dbl>      <dbl>   <dbl>
 1                           0        969    62.3
 2                           1        505    32.5
 3                           2         58     3.7
 4                           3         12     0.8
 5                           4          1     0.1
 6                           5          3     0.2
 7                           6          3     0.2
 8                          10          2     0.1
 9                          15          1     0.1
10                          17          1     0.1
11                          28          1     0.1
12                          85         12     0.9

Next, we add a new variable so that it is easy to plot and interpret the number of deaths for each percentage.

We will add the word “deaths” to each value in the Killed (includes shooter) variable using the base paste0() function. Note that this function automatically will result in no space or any other character between pasted elements. The paste() function can alternatively be used for those cases.

deaths_perc_event %<>% 
  mutate(category = paste0(`Killed (includes shooter)`, 
                           " deaths ", "(", percent, "%)")) 

deaths_perc_event
# A tibble: 12 x 4
   `Killed (includes shooter)` num_events percent category        
                         <dbl>      <dbl>   <dbl> <chr>           
 1                           0        969    62.3 0 deaths (62.3%)
 2                           1        505    32.5 1 deaths (32.5%)
 3                           2         58     3.7 2 deaths (3.7%) 
 4                           3         12     0.8 3 deaths (0.8%) 
 5                           4          1     0.1 4 deaths (0.1%) 
 6                           5          3     0.2 5 deaths (0.2%) 
 7                           6          3     0.2 6 deaths (0.2%) 
 8                          10          2     0.1 10 deaths (0.1%)
 9                          15          1     0.1 15 deaths (0.1%)
10                          17          1     0.1 17 deaths (0.1%)
11                          28          1     0.1 28 deaths (0.1%)
12                          85         12     0.9 85 deaths (0.9%)

We can change the value for the last row about the events that resulted in more than 4 deaths.

We can use the last() function from the dplyr package combined with the pull() function to specifically grab this value.

last(pull(deaths_perc_event, category))
[1] "85 deaths (0.9%)"

Using the case_when() function, we can change this value:

deaths_perc_event %<>% 
  mutate(category =
           case_when(category == last(pull(deaths_perc_event, category)) ~ 
                        paste0("4+ deaths ", "(", percent, "%)"),
                     category == "1 deaths" ~ "1 death",
                     TRUE ~ category))

deaths_perc_event
# A tibble: 12 x 4
   `Killed (includes shooter)` num_events percent category        
                         <dbl>      <dbl>   <dbl> <chr>           
 1                           0        969    62.3 0 deaths (62.3%)
 2                           1        505    32.5 1 deaths (32.5%)
 3                           2         58     3.7 2 deaths (3.7%) 
 4                           3         12     0.8 3 deaths (0.8%) 
 5                           4          1     0.1 4 deaths (0.1%) 
 6                           5          3     0.2 5 deaths (0.2%) 
 7                           6          3     0.2 6 deaths (0.2%) 
 8                          10          2     0.1 10 deaths (0.1%)
 9                          15          1     0.1 15 deaths (0.1%)
10                          17          1     0.1 17 deaths (0.1%)
11                          28          1     0.1 28 deaths (0.1%)
12                          85         12     0.9 4+ deaths (0.9%)

Question Opportunity

We could of used thestr_replace() function from the stringr package to replace the value for the last row. This function would directly change the value of “85 deaths” to “4+deaths”, but this would not be as reproducible. Why is that?


Click here to reveal the answer.

Say we used this code again after the data got updated. Then there may be more deaths in this category and therefore this value would no longer be “85 deaths”. Instead, by using case_when(), we can use an expression for the last value of the deaths_perc_event tibble and replace that, regardless of what the value is, with “4+deaths”. Recall that case_when() replaces all other values that are not specified with NA. We do not want to lose the other values for the category variable. So to avoid this, we assign each of the values that are not the last value or the "1 deaths" value to what they currently are for the category variable, using TRUE ~ category (Note that all remaining unassigned values are indicated as TRUE).

We could also actually type out the percentage of 4+death cases, but it is always more reproducible to instead use an expression that will evaluate to the value we want. This way if we were to update our data with additional school shooting events, this evaluation would also update.


OK, this looks as we hoped. OK, now we are ready to make plots.

Let’s start with the pie chart. Historically, this has become a bit controversial type of plot. However, it can be very useful when you are actually looking at percentages and the goal is to see major trends in the data, such as all the groups are roughly equal or one group is particularly larger than the rest. When this is the case and you are presenting the data to an audience that is less familiar with data science, they may expect to see a pie chart. Thus it is useful to know how to make one. However, in most other cases pie charts do a poor job at allowing us to see more subtle differences, and they are particularly confusing when we are not looking at proportions, but raw counts. In those cases it is better to use a bar chart as we have already done.

There is no geom_* function that allows you to create a pie chart directly. Instead we will create our bar plot as we have and then use the coord_polar() function to wrap our y axis into a circular shape.

deaths_perc_event %>%
  filter(percent>0.5) %>%
  ggplot(aes(x = "", y = percent, fill = category)) +
      # adding color here adds a black outline
    geom_col(color = "black") +
    coord_polar("y", start = 0) +
    scale_y_continuous(breaks= NULL) +
    theme_minimal() +
    theme(axis.title = element_blank()) +
    scale_fill_viridis_d() +
    labs(title = "Percentages of school shooting deaths\n(including the shooter)")

This is actually a fairly easy plot to interpret. We can see that most events resulted in zero deaths and that the next largest proportion resulted in one death, while a sizable but small proportion resulted in two deaths. A very small proportion resulted in three or four or more deaths.

We also can create a waffle plot. This plot offers one advantage over the pie chart, in that it also allows for easier interpretation of more subtle proportion differences while also showing big picture differences in efficient manner.

First, we filter for only the data that we want to plot. We only want the 0,1,2,3, or 4+ categories. We can do so by using the str_detect() function from the stringr package. This allows us to find the values that match multiple patterns. The patterns are separated by the | or operator. Thus any value matching any of the patterns should be kept. Notice that the \\ is necessary before the + so that is not interpreted as a mathematical plus sign.

The waffle() function requires that the data be in wide format. Thus we need to use pivot_wider() of the tidyr package to do so. This is very similar to the pivot_longer() function, however in this case we need to specify what existing column contains the names for the new columns using names_from and what existing column contains the values for the new columns using values_from.

deaths_perc_event %>% 
  select(-`Killed (includes shooter)`) %>%
  filter(str_detect(category, "0 deaths|1 death|2 deaths|3 deaths|4\\+")) %>%
  mutate(percent = round(percent)) %>%
  select(-num_events) %>%
  tidyr::pivot_wider(names_from = category, 
                     values_from = percent) %>%
  waffle::waffle(legend_pos = "bottom", title="Deaths Per School Shooting", 
                 xlab="1 square ~ 1%") +
  scale_fill_viridis_d()

Percentages

We are also interested in including statistics in our dashboard. For example, we are interested in how many shooters committed or attempted suicide.

We previously converted variables with yes or no answers because they were inconsistently coded as yes/ y and no/n. Furthermore, logical variables are easier to work with in terms of performing calculations because TRUE values are treated like a 1 while FALSE values are treated like a 0.

We can calculate the percentage of shooters that committed or attempted suicide out of all entries that have data for this information. Thus we do not want to include NA values in the calculation, otherwise this might give us a distorted picture of the truth.

Let’s take a look at the data for this variable:

shooting_data %>% 
  count(`Suicide (or attempted suicide) by Shooter (Y/N)`)
# A tibble: 3 x 2
  `Suicide (or attempted suicide) by Shooter (Y/N)`     n
  <lgl>                                             <int>
1 FALSE                                              1308
2 TRUE                                                203
3 NA                                                   45

We can see that there are 45 NA values.

If we calculate a sum of the TRUE values, (which are those that are equivalent to 1), we can do so by just summing this variable, which is equivalent to summing values that are greater than 0.

sum(pull(shooting_data, 
         `Suicide (or attempted suicide) by Shooter (Y/N)`), 
    na.rm = TRUE)
[1] 203
sum(pull(shooting_data, 
         `Suicide (or attempted suicide) by Shooter (Y/N)`) > 0, 
    na.rm = TRUE)
[1] 203

In contrast, FALSE values are those that are equivalent to 0. Thus if we want to divide by the sum of all values that are FALSE are TRUE, then we can sum all values greater than or equal to 0.

sum(pull(shooting_data, 
         `Suicide (or attempted suicide) by Shooter (Y/N)`) >= 0, 
    na.rm = TRUE)
[1] 1511

Thus, we can calculate the percentage of all reporting values like so, where the TRUE values are divided by the sum of all TRUE and FALSE values: (We also multiply by 100 using *100 to get the percentage value.)

suicide <- 
  (sum(pull(shooting_data,`Suicide (or attempted suicide) by Shooter (Y/N)`), na.rm = TRUE) /
   sum(pull(shooting_data, `Suicide (or attempted suicide) by Shooter (Y/N)`)>=0, na.rm = TRUE))*100

suicide
[1] 13.43481

We can use the round() function to round this value and the format() to make sure that the value has the correct number of digits.

suicide <- round(suicide, 2)
suicide
[1] 13.43

If after rounding we wanted zeros after the decimal so that the number of digits after the decimal was consistent for the different statistics we were displaying, we could use the format() function to specify this.

So we can add a zero after 13.4 like so:

format(suicide, nsmall = 2)
[1] "13.43"

To calculate the percentage of school shootings where this information was reported we can do the following, by calculating all values that are not NA using >=0 and calculating the number all possible values using the base length() function.

reporting_suic <- 
  (sum(pull(shooting_data, 
            `Suicide (or attempted suicide) by Shooter (Y/N)`)>=0, 
       na.rm = TRUE) /
   length(pull(shooting_data, 
               `Suicide (or attempted suicide) by Shooter (Y/N)`))
   )*100

reporting_suic <- round(reporting_suic, 1)
reporting_suic
[1] 97.1

We can see that 97% of the school shootings have information about this variable.

It is important to check and report this percentage so that people can better understand if our percentages are reliable. If only 2% of the school shootings had this information and in all cases of the 2% the school shootings involved a suicide (or attempt), then this would lead people to believe that 100% of school shootings involve a shooter suicide (or attempt). This would clearly be misleading! In our case the majority of the school shootings included this information, so we will indeed report the percentage and we will also let people know how much of the school shooting data had this information.

Question Opportunity

Now try to perform variations of these calculations to calculate other statistics for our dashboard, such as the percentage of the shooters that were male or the percentage of events where a single handgun was used, (hint the Firearm Type value will be Handgun).


Shooter Was Male


Click here to reveal the code.
gender <- paste(as.character(round(100 * (sum(
    case_when(pull(shooting_data,`Shooter Gender`) == "Male" ~ TRUE,
                                                        TRUE ~ FALSE),
                                      na.rm = TRUE)
    /
      sum(pull(shooting_data, `Shooter Gender`)>=0, na.rm = TRUE)),
    1)), "%")

reporting_male <- (sum(pull(shooting_data, `Shooter Gender`)>=0, na.rm = TRUE)/
              length(pull(shooting_data, `Shooter Gender`)))*100
reporting_male <- round(reporting_male, 1)


gender
[1] "95.1 %"
reporting_male
[1] 88.6

Use of a Single Handgun


Click here to reveal the code.
handgun <-paste(as.character(round(100 *(sum(case_when(
      pull(shooting_data,`Firearm Type`) == "Handgun" ~ TRUE,
                                                 TRUE ~ FALSE), na.rm = TRUE)
    /
      sum(pull(shooting_data, `Firearm Type`)>=0, na.rm = TRUE)),
    1)), "%")

reporting_gun <- (sum(pull(shooting_data, `Firearm Type`)>=0, na.rm = TRUE)/
              length(pull(shooting_data, `Firearm Type`)))*100
reporting_gun <- round(reporting_gun, 1)

handgun
[1] "81.7 %"
reporting_gun
[1] 83.4

Dashboard Basics


We are now ready to build our dashboard!

Let’s introduce some basics about creating dashboards in R in the flexdashboard package.

Note that you can also start the case study at this point, we will let you know how to get the data that you need.

Dashboard packages


To make our dashboard we will use three very useful packages:

  1. flexdashboard

Flexdashboard is a package that was created by RStudio and released in May of 2016. This package allows for users to more easily create dashboards using R Markdown.

See here for a video about flexdashboard and here for a more information on how to use this package.

  1. leaflet

Leaflet is the leading open-source JavaScript library for interactive maps and is used by many websites. The leaflet R package allows for users to more easily integrate leaflet maps in R, to create maps like the one below. We will use this package to create a map of where school shootings have occurred in the US.

Here is an example of an interactive map made with leaflet

  1. shiny

Shiny is an R package that makes it easier to create interactive web applications in R. See here for a gallery of examples. People have created a variety of diverse applications using this package- from interactive websites to games.

Here is an screenshot of a shiny app.

[source]

See here for a list of other packages that are useful for adding elements to dashboards created with the flexdashboard package.


R Markdown


The case study that you are reading right now was created using an R Markdown document. This means that it is a document that uses the Markdown language syntax with enhanced capabilities of executing R code in the document.

In fact, if you click the button that says “code” on the upper right corner at the top of the HTML you will download the R Markdown document for this case study.

R Markdown (Rmd) is a file format that contains Markdown syntax and embedded R code (it can also incorporate code from some other languages like Python and SQL).

Click here to see how this video was embedded in this R Markdown.

This video was included using the vembedr package. Videos on Vimeo or YouTube can be added like so, where a url is added within quotation marks and the following two lines of code allow for the video to be centered in the R Markdown output. See [here](vembedr to learn more about embedding videos with this package.

library(vembedr)
embed_url("https://vimeo.com/178485416") %>%
  div(class = "vembedr") %>%
  div(align = "center")

These Rmd files can be rendered into a variety of file outputs like PDF, word, HTML etc. by the knitr and rmarkdown packages.

This relies on conversion of the Rmd file into the Markdown language by software called Pandoc.

Markdown (which has been implemented by many languages, such as Perl, Java, Python, C#, Ruby, etc.) is a language of a particular class of programming languages called lightweight markup languages (LML).

LMLs have relatively simple and intuitive syntax, and are therefore relatively easy to write and read and are converted by software into some type of less human-friendly language to create an output document like a PDF or an HTML file. In fact, multiple output files can be created from the same LML file!

In our case we are interested in rendering our Rmd document into a website. The code in our R Markdown document will be interpreted and converted ultimately into HTML code.

Although LMLs tend to be quite similar, here you can see some of the differences in syntax:

[source]

See this book for more information on working with R Markdown files.

The RStudio cheatsheet for R Markdown and this tutorial are great for getting started.

Flexdashboard


There are several important features about the R Markdown language that the flexdashboard package leverages.

These features are used to specify the layout and elements of the dashboard.

Here are some major R Markdown features to keep in mind for flexdashboard:

  1. The beginning of an R Markdown document is what is called the YAML header. This is delineated by --- three dash marks before and after the header YAML code.

Like so:

[source]

YAML is yet another language, but unlike Markdown it is a data-oriented language and is often used for the configuration of software or to set up how a software program should work.

Whatever code you put in the YAML header will influence the rest of the document and essentially set up how the R Markdown document will render. In the example above, the type of output is specified.

Other more complicated features can be included. For example, we can specify that we are creating a dashboard with flexdashboard and we can specify how we want the layout of our dashboard to be displayed like so:

[source]

We will describe this in more detail soon.

  1. To add a page to a navigation bar (also called a navbar) the following syntax is used =======. The number of dashes does not matter. (This is a level 1 header in Markdown, just like #)

  2. To add columns or rows the following syntax is used ---------. By default this notation will create new columns, however if the YAML is modified to specify to create rows, than this same syntax will be used to create rows. The number of dashes does not matter. (This is a level 2 header in Markdown, just like ##)

  3. Components within the dashboard are delineated by using ### - if you are familiar with Markdown notation, this is a level 3 Markdown header.

If this includes text like so: ### text, this adds header text to the component, however this is not required.

  1. To include a plot or any output from R, use the following syntax: "```{r}" on it’s own line followed by your code, followed by "```". This creates what is called a code chunk.

[source]
  1. Another component of flexdashboard is value boxes. These are essentially text boxes for statistics or text that you might like to feature or emphasize. To do this again the ### syntax is used to put a text label describing what the value box contains followed by a code chunk that uses the valueBox() function from the flexdasboard package. The value to display is specified using the value argument, as well as optional other aspects using additional arguments, such as the color of the value box using the color argument like the example below:
### ValueBoxText

'''{r}
valueBox(value = 10
  color = "white")

'''

Note: in our examples of code we will use "'''" instead of "```". This is only to allow for easy viewing of examples. All code chunks require "```".

Here you can see a more thorough example which includes icons:

[source]
  1. Instead of value boxes you can also include a slight variation called a gauge. These are created with the guage() function from the flexdashboard package. This requires numeric values for a value, a min, and a max argument. Optionally, a symbol can also be added with the symbol argument. The value argument does not have to be explicitly called though, which is also true of the valueBox() function.

Here is a simple example:

### GuageText

'''{r}
flexdashboard::gauge(value = 10, 
                       min = 0, 
                       max = 100, 
                    symbol = "%")

'''

This creates the following output:

Here is a more complicated example:

Layout


Adding Columns


To add multiple columns the following syntax is used --------- for each column and nothing additional is required in the header.

Additional features about the columns, such as the width can be specified using brackets{}like in the example below. Note that the word Column isn’t necessary. In this example two columns are created that will be oriented next to one another and elements within the columns will be placed top to bottom.

Adding Rows


To add multiple rows - the yaml needs to state that the orientation is for rows instead of for columns (see the image below), and then the same syntax is used --------- for each row instead of columns. In this example, two rows are created that will be oriented on top of one another and elements within the rows will be placed next to each other.

Again the word Row is not actually necessary.

[source]

See here for template options.

Tabs


To add tabs columns/rows we can use the following:

Column {.tabset}

In this example, two columns are created and then two tabs are added to the second column.

Shiny


Interactive elements can be added to dashboards. In our dashboard, we will use packages such as DT and leaflet that have shiny functionality. This requires that shiny is enabled in the YAML header by including runtime:shiny in the YAML.

Here is an example of a YAML that includes this:


Deployment


You have a few options to publish your dashboard:

  1. If your dashboard is not interactive (does not use shiny) or uses certain widgets like the datatable() function from the DT package, you just need to knit your R Markdown file into an html file.

Then you can host this on GitHub if you choose by changing the GitHub Pages settings of your repository:

  1. If your dashboard is interactive (uses shiny), you can host it on https://www.shinyapps.io/ after making an account. To do this you need to install the rsconnect package and after you have made an account and configured it, you can use the publish button of the RStudio IDE which looks like this on the upper right corner:

Note that this also requires authentication using tokens.

See this link for a getting start for this process.

  1. You can also publish using RStudio Connect. This also involves creating an account and pushing the publish button.

Question Opportunity

Let’s take a minute to test your knowledge about flexdashboard basics:

  1. How do we create multiple pages?
  2. How do we create multiple columns?
  3. How do we create multiple tabs?
  4. How do we start creating a dashboard?
  5. How do we enable our dashboard to be interactive?

Click here to reveal the answers.
  1. How do we create multiple pages? We use the === syntax.
  2. How do we create multiple columns? We use the --- syntax.
  3. How do we create multiple tabs? We use {.tabset} syntax combined with the column break --- syntax.
  4. How do we start creating a dashboard? We create an R Markdown document and we add output: flexdashboard::flexdashboard to the YAML.
  5. How do we enable our dashboard to be interactive? We add runtime:shiny to the YAML.

Our Dashboard


OK! Now that we know a bit about the basics of creating a dashboard, let’s create our own.

The link to the dashboard described in this section is located here.

We want to create a dashboard that has several tabs that will look like this:

Getting started


The first thing we need to do to create our dashboard is to create a new .Rmd document like so in R Studio:

YAML header


Next we need to update the YAML header to look like this:

As you might expect, title: indicates the title of our dashboard.

The output: line specifies what type of output we want the .Rmd file to be rendered.

We need to include flexdashboard::flex_dashboard: as the output to create a dashboard with the flexdashboard package. This can be included on the same line as output: or on the next line with a preceding tab.

Note: that YAML is sensitive to spacing, thus this tab is required to get the proper output.

The next four lines are arguments for how the dashboard should be created.

  1. logo: allows you to include a logo on top of your dashboard. With this theme this will be in the upper left corner. The logo we chose to use came from here, but you could theoretically use any png of appropriate size.

  2. theme: allows you to specify how the dashboard will look in general. Note that this can be used to modify the general look of any type of R Markdown output, not just dashboards created with flexdashboard. See here for a list of options. In our case, the theme is called readable and will create documents that look like this:

[source]
  1. orientation: the options are columns or rows and specifies if the -------- syntax creates rows or columns for the layout. This is not necessary if the option is columns.

  2. source_code: specifies if a URL will be included as a navigation bar item with access to the source code.

  3. vertical_layout: The options are fill or scroll. Fill causes the charts to re-size to fill the page, while the scroll option renders plots as their natural height which may or may not require scrolling the page.

There are many other argument options for how the dashboard is displayed.

You can run the following command in the console to see more information about the arguments in the help pane of the R Studio IDE.

?flexdashboard::flex_dashboard()

Also see the CRAN documentation for more details.

Loading the packages and data


Since we are creating our dashboard in a new Rmd file, we need to load the necessary packages and the wrangled data that we created in this Rmd file. In that Rmd file, it looks something like this.

Note: all the following code would be added to the Rmd file for the dashboard and are simply shown here for illustrative purposes.

library(here)
library(readr)
library(dplyr)
library(flexdashboard)
library(shiny)
library(magrittr)
library(forcats)
library(stringr)
library(waffle)
library(tidyr)
library(poliscidata)
library(leaflet)
library(htmltools)
library(DT)

For more information about what these packages were used for, see the beginning of this case study and the [Helpful Links] section. The data can be found and downloaded from our GitHub repository at this link. In our case we saved this to a subdirectory called wrangled within a directory called data of our working directory. We recommend using RStudio projects and the here package to make navigating to files easy and reproducible.

shooting_data <- 
  read_csv(here("data", "wrangled",
                "shooting_data_wrangled_pre_map.csv"))

shooting_data_for_map <- 
  read_csv(here("data", "wrangled",
                "shooting_data_wrangled_for_map.csv"))

Creating pages


Recall that === is used to designate elements that are part of the navigation bar.

We want 7 items besides the source code (which was added automatically based on the YAML code).

First, we create 7 divisions for these main pages. We add icons to each from Font Awesome.

Use this link to find other icon options. If you click on the “start using this icon” button it will take you to a page with HTML code like this:

[source]

Only the fa-database portion is required in the brackets after data-icon= to add the icon to the navigation bar.


About {data-icon="fa-question-circle"}
====================================

The Data {data-icon="fa-database"}
===================================== 

US Statistics {data-icon="fa-flag"}
=====================================

State Statistics {data-icon=fa-flag-checkered}
====================================

Map {data-icon="fa-map"}
====================================

Tutorial {.storyboard data-icon="fa-list-ol"}
====================================

Hotline {data-icon="fa-exclamation-triangle"}
====================================

The About Page


Here, we create content in the About page.

Look


This is what the page will look like:

Overall Structure


Here is the overall structure for this page:

Details



Click here if you would like to see all of the code for this page.

On this page we will have two columns - one which will be wider than the other. Size specifications on flexdashboard are unit-less; the width of any column included on a page is a function of the width set for a column against the sum of widths for all columns on that page. If we set columns sizes of 600 and 300 on a page with two columns, one column will be twice as large as the other column. We want the left column to be quite a bit larger than the right, so we will set the left as 70 and the right as 30.

Question Opportunity

Can you recall how we would make these columns?


Click here to reveal the code.
About {data-icon="fa-question-circle"}
===================================== 

Column {data-width = 70}
-------------------------------------

###

Column {data-width = 30}
-------------------------------------

###

Recall that ### is used to add elements to columns and rows. Note that there is no text next to the ### syntax that designates an element of our dashboard. In the previous examples, a header was used like so ### header:

[source]

We do not actually want a header now, so we can simply use ### without any text following it. Note that you can get away with not using the ###, but some elements will not render properly.

Next, we add a block of text describing the dashboard to the first column and we will add an image to the second column like the following. Notice that two asterisks ** around text makes them appear as bold and one * makes it appear as italic. See this RStudio cheatsheet for some basic Markdown syntax for stylizing text:

[source]

This is what the code for this page looks like (notice that there is an internal link to the Tutrial page):

About {data-icon="fa-question-circle"}
===================================== 

Column {data-width=70}
-------------------------------------

### 

**What is the purpose of this dashboard?**

This dashboard has two purposes:

1. To illustrate trends in school shooting events in the United States
2. To demonstrate how to create a dashboard using `R`

**The data**

This dashboard uses data from the open-source [K-12 Shool Shooting Database](https://www.chds.us/ssdb/dataset/) downloaded from the [Center for Homeland Defense and Security](https://www.chds.us/c/) at the at the [Naval Postgraduate School(NPS)](https://en.wikipedia.org/wiki/Naval_Postgraduate_School). This data was downloaded June of 2020.

<style>
div.green { background-color:#8FBC8F; border-radius: 5px; padding: 20px; font-size: 1em;color: white;}
</style>
<div class = "green">
Riedman, David, and Desmond O’Neill. “CHDS – K-12 School Shooting Database.” Center for Homeland Defense and Security, June 2020, [www.chds.us/ssdb](www.chds.us/ssdb).
</div>


  
This database includes information about school shooting events for students in grades K-12 in the United States dating back to 1970. The database has additional information not shown on our dashboard including, but not limited to: location of the event at the school, source for the shooting information, shooter characteristics, and victim characteristics. 

### 


<u>**Want to learn how to create a dashboard just like this?**</u>

Visit the [*Tutorial*](#tutorial) page of this dashboard to first learn the basics about building a dashboard with the `flexdashboard` package.

At the end of the tutorial we provide a link to this [supplementary resource by the Open Case Studies project](https://opencasestudies.github.io/ocs-bp-school-shootings-dashboard/), which provides more detailed information about how ***this dashboard*** was created.

<style>
div.blue { background-color:#e6f0ff; border-radius: 5px; padding: 20px; font-size: .8 em;}
</style>
<div class = "blue">

 **Acknowledgements**

This was created as part of the [Open Case Studies](https://opencasestudies.github.io){target="_blank"} project. We would like to acknowledge the [Bloomberg American Health Initiative](https://americanhealth.jhu.edu/) for funding this work. 

 **Disclaimer**

This dashboard uses data from the [K-12 Shool Shooting Database](https://www.chds.us/ssdb/about/). We acknowledge (like their website) that there may be reporting errors. The trends and statistics shown do not account for the many other factors that may influence the occurrence of shooting events. The dashboard should not be used in the context of making policy decisions without external consultation from scientific experts. 


 **License**

This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 [(CC BY-NC 3.0)](https://creativecommons.org/licenses/by-nc/3.0/us/){target="_blank"} United States License.
</div>

Column {data-width=30}
-------------------------------------

###


'''{r, echo=FALSE, fig.cap="[Photograph by Nathan Dumlao](https://unsplash.com/photos/xPHmmVKS8lM)"}
knitr::include_graphics(here::here("img", "nathan-dumlao-xPHmmVKS8lM-unsplash.jpg"))
'''

Note that we will use "'''" for to show code chunks of the actual code from the dashboard.

The image used in this second column is from a website called unsplash (https://unsplash.com/) which hosts images for free use but includes information about the photographer if you chose to credit them. A short link for this image was found by clicking on it and then clicking the share button.

Notice the echo = FALSE specification for the code chunk which causes the code to be evaluated but but not shown, while fig.cap adds the figure caption.

The image is included using the include_graphics() function from the knitr package. We need to specify where this image is located for this to work. You can do this without specifying a path if the image file is in the same directory as your .Rmd file that you are using to create your dashboard. However using the here() function from the here package we can organize our files a bit. This function will automatically start the path wherever we have included an RStudio project file, this can be done in RStudio like so:

If you are new to using RStudio projects, please see this link for more information.

Then if we create a directory or folder called img and place our image files in this directory, then we can specify the full path to this file on our computer, by just using here::here("img", "name_of_image.png"). The include_graphics() function works for a variety image file types.

Also you may have noticed the <style> html code to add a blue and green background to portions of the text.

The text that we want altered with this particular style is delineated by the <div> to start and the </div> to end the style.

Let’s take a look at the first one to explain what is happening here:

<style>
div.green { background-color:#8FBC8F; border-radius: 5px; padding: 20px; font-size: 1em; color: white;}
</style>
<div class = "green">
Riedman, David, and Desmond O’Neill. “CHDS – K-12 School Shooting Database.” Center for Homeland Defense and Security, June 2020, [www.chds.us/ssdb](www.chds.us/ssdb).
</div>

The instructions for the style are within the <style> and </style> content dividers. Inside these dividers is CSS code, which is what is used to stylize HTML. The div.green is the name of this particular style which involves a particular background color (#8FBC8F - see here for more options), with a boarder radius of 5 pixel to round the edges of the background color around the text with a size 5 pixel radius. The code also states that a padding specification for the size of the margins of the text box around the text and it specifies that font should be of 1 em units (which stands for element - thus 1 unit relative to the size of the element) and that the font should be white.

The div.green specifies that green is the name of this style, thus we can then use <div class = green> (called a CSS selector) to style the text this way. This can then be used again any time we want this style like so:

<div class = "green">

text 

</div>

See this website to learn more about HTML and CSS.


The Data Page (Interactive)


Let’s create a page about the data that we are using.

Look


This is what the page will look like:

Overall Structure


Here is the overall structure for this page:

Details



Click here to see the code for this page.

To create the structure for this page that will display the data, we have two columns, with the first one (on the left) wider than the other. Again we have a block of text in the column on the left like so:

The Data {data-icon="fa-database"}
===================================== 


Column {data-width=70}
-------------------------------------

###

The data used in this dashboard is from the [**Center for Homeland Defense and Security (CHDS)**](Center for Homeland Defense and Security (CHDS)) [**K-12 Shool Shooting Database**](https://www.chds.us/ssdb/about/). 

Their methods for identifying and authenticating incidents are outlined [here](https://www.chds.us/ssdb/methods/).

According to their website: 

*"The database compiles information from more than 25 different sources including peer-reviewed studies, government reports, mainstream media, non-profits, private websites, blogs, and crowd-sourced lists that have been analyzed, filtered, deconflicted, and cross-referenced. **All of the information is based on open-source information and 3rd party reporting... and may include reporting errors.**"*

***


Column {data-width=30}
-------------------------------------

###

Now we will add our DT_table to the first column. First, we need to include the code that we previously used to create the DT_table in our dashboard .Rmd file:

DT_table <- shooting_data %>%
  dplyr::select(Date,
                School,
                City,
                State,
                `Killed (includes shooter)`,
                `Narrative (Detailed Summary/ Background)`) %>%
  rename("Deaths" = `Killed (includes shooter)`) %>%
  rename("Narrative" = `Narrative (Detailed Summary/ Background)`)

We then include some code to render this interactive table in our dashboard. Since we have shiny enabled in our YAML header, we can use the renderDataTable() of the DT package to produce the output we desire.

We also want to use the options argument to specify how the data is rendered. The scroller = TRUE argument adds a scroll bar to the table, the scrollY argument specifies that the scroll bar should be for they Y axis direction (up and down) of the table and specifies how large the scroller should be, the pageLength argument specifies how many rows should be displayed simultaneously within the table, and the autoWidth = TRUE argument specifies that the table should fit the space of the column or page it is within.

We will also add a caption with a link to the original data using the tags() and withTags() functions of the htmltools package. Different options for types of tags can be selected using the $.

DT::renderDataTable({
  DT::datatable(DT_table,
                caption = htmltools::tags$caption(
                  style = 'caption-side: top; text-align: Left;',
                  htmltools::withTags(
                    div(HTML('<a href="https://www.chds.us/ssdb/about/)">Click here to be redirected to a page where this data can be downloaded.</a>')))),
                  options = list(autoWidth = TRUE,
                                 pageLength = 10,
                                 scroller = TRUE,
                                 scrollY = '450px'))
})

We will also add another image to the column on the right, overall the code looks like this:


The Data {data-icon="fa-database"}
===================================== 

Column {data-width=70}
-------------------------------------

###

The data used in this dashboard is from the [**Center for Homeland Defense and Security (CHDS)**](Center for Homeland Defense and Security (CHDS)) [**K-12 Shool Shooting Database**](https://www.chds.us/ssdb/about/). 

Their methods for identifying and authenticating incidents are outlined [here](https://www.chds.us/ssdb/methods/).

Previously, according to their website: 

*"The database compiles information from more than 25 different sources including peer-reviewed studies, government reports, mainstream media, non-profits, private websites, blogs, and crowd-sourced lists that have been analyzed, filtered, deconflicted, and cross-referenced. **All of the information is based on open-source information and 3rd party reporting... and may include reporting errors.**"*

***

'''{r, echo=FALSE}
# Create the DT table first
DT_table <- shooting_data %>%
  dplyr::select(Date,
                School,
                City,
                State,
                `Killed (includes shooter)`,
                `Narrative (Detailed Summary/ Background)`) %>%
  rename("Deaths" = `Killed (includes shooter)`) %>%
  rename("Narrative" = `Narrative (Detailed Summary/ Background)`)
# Instead of depending on the st_jitter algorithm to generate random placement, a custom function placing the points side by side at a set distance could be used to make points occuring at the same location appear neatly apart.
'''

'''{r, echo=FALSE}
DT::renderDataTable({
  DT::datatable(DT_table,
                caption = htmltools::tags$caption(
                  style = 'caption-side: top; text-align: Left;',
                  htmltools::withTags(
                    div(HTML('<a href="https://www.chds.us/ssdb/about/)">Click here to be redirected to a page where this data can be downloaded.</a>')))),
                options = list(autoWidth = TRUE,
                               pageLength = 10,
                               scroller = TRUE,
                               scrollY = '450px'))
})
'''

Column {data-width=30}
-------------------------------------

###

'''{r, echo=FALSE, fig.cap="[Photograph by Rubén Rodriguez](https://unsplash.com/photos/IXTvnOOSTyU)"}
knitr::include_graphics(here::here("img", "ruben-rodriguez-IXTvnOOSTyU-unsplash.jpg"))
'''

The US Statistics Page


Let’s create a page for US Statistics we would like to share.

Look


This is what the page will look like:

Overall Structure


Here is the overall structure for this page which uses a tab layout:

Details



Click here to see the code for this page.

Here we use the .tabset and .tabset-fade options specified for our first column.

US Statistics {data-icon="fa-flag"}
===================================== 


Column {data-width=70 .tabset .tabset-fade}
-------------------------------------

After having specified the .tabset and .tabset-fade options, we can create new tabs in the same way we would add elements to our dashboard with the ### syntax. Just like in this example:

[source]

Let’s make a tab for yearly school shooting events and deaths, a tab for cumulative school shooting events and deaths, and a tab about the number of deaths per school shooting. In each tab, we will include the code for the plots that we have previously created.

US Statistics {data-icon="fa-flag"}
===================================== 

Column {data-width=700 .tabset .tabset-fade}
-------------------------------------

### Yearly Deaths and Shootings

'''{r}

start <- 1970
end <- 2020

shootings_per_year<- shooting_data %>%
    group_by(Date_year) %>%
    count() %>%
  rename("Shootings" = n) %>%
    ungroup()

deaths_per_year<-shooting_data %>% 
  group_by(Date_year) %>%
  summarize(Deaths =sum(`Killed (includes shooter)`))


per_year<-full_join(shootings_per_year, deaths_per_year)
per_year %<>%pivot_longer( cols = (-Date_year), 
                           values_to = "events", 
                           names_to = "id")

per_year%<>% 
  mutate(id = forcats::fct_inorder(id))

per_year %>%
    ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col()+
    facet_wrap(~id, scales = "free", 
               labeller = as_labeller(c(Shootings = "Shootings (# of events)", 
                                        Deaths = "Deaths (# of people)")), 
               strip.position = "left")+
    scale_x_continuous(breaks = seq(start, end, by = 5),
                 labels = seq(start, end, by = 5),
                 limits = c(start-1, end+1)) +
    scale_y_continuous(breaks = seq(0, 120, by = 30),
                 labels = seq(0, 120, by = 30),
                 limits = c(0, 121))+
    theme_minimal() +
   labs(title = "Yearly Shootings and Deaths Attributable to School Shootings",
         subtitle = "United States",
         y = NULL,
         x = "Year")+
  scale_fill_manual(values = c("black", "black"))+
  theme(legend.position = "none", 
        legend.title = element_blank(),
        axis.text.x = element_text(angle = 90, face = "bold"),
        axis.title.x = element_text(face = "bold", size = 14),
        strip.background = element_blank(),
        strip.placement = "outside",
        strip.text = element_text(face = "bold", size = 14))


theme_dashboard <- function(){ 
  theme(legend.position = "none", 
        legend.title = element_blank(),
        #title = element_text(face = "bold", size = 16),
        axis.text.x = element_text(angle = 90, face = "bold"),
        axis.title.x = element_text(face = "bold", size = 14),
        strip.background = element_blank(),
        strip.placement = "outside",
        strip.text = element_text(face = "bold", size = 14))
}
'''

### Yearly Cumulative Deaths and Shootings

'''{r}
shootings_per_year_cum <- 
    shootings_per_year %>%
    mutate(Shootings = cumsum(Shootings))

deaths_per_year_cum <- 
    deaths_per_year %>%
    mutate(Deaths = cumsum(Deaths))

per_year_cum <- full_join(shootings_per_year_cum, deaths_per_year_cum)

per_year_cum %<>% 
  pivot_longer(cols = c(Shootings, Deaths ), 
               values_to = "events", 
               names_to = "id")

per_year_cum %<>% 
  mutate(id = forcats::fct_inorder(id))

per_year_cum %>%
  ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col()+
    facet_wrap(~id, scales = "free", 
               labeller = as_labeller(c(Shootings = "Shootings (cumulative # of events)", 
                                        Deaths = "Deaths(cumulative # of people)")), 
               strip.position = "left")+
    scale_x_continuous(breaks = seq(start, end, by = 5),
                       labels = seq(start, end, by = 5),
                       limits = c(start-1, end+1)) +
    scale_fill_manual(values = c("black", "black")) +
    theme_minimal() +
    labs(title = "Cumulative Yearly Shootings and Deaths Attributable to\nSchool Shootings",
         subtitle = "United States",
         y = NULL,
         x = "Year") +
    theme_dashboard() 
'''

### Deaths Per Shooting

'''{r}
deaths_perc_event <- 
   shooting_data %>%
   count(`Killed (includes shooter)`) %>%
   rename("num_events"= n) %>%
   mutate(percent = round(num_events/sum(num_events)*100, digits =1))

greater_than4 <- 
  deaths_perc_event %>% 
  filter(`Killed (includes shooter)` >= 4) %>% 
  colSums()

deaths_perc_event %<>% bind_rows(greater_than4)

deaths_perc_event %<>% 
  mutate(category = paste0(`Killed (includes shooter)`, " deaths ", "\n(", percent, "%)")) 

deaths_perc_event %<>% 
  mutate(category = case_when(
    category ==  last(pull(deaths_perc_event, category)) ~ paste0("4+ deaths ", "\n(", percent, "%)"),
    category == "1 deaths" ~ "1 death",
    TRUE ~ category))

deaths_perc_event %>% 
  select(-`Killed (includes shooter)`) %>%
  filter(str_detect(category, "0 deaths|1 death|2 deaths|3 deaths|4\\+")) %>%
  mutate(percent = round(percent)) %>%
  select(-num_events) %>%
  tidyr::pivot_wider(names_from = category, 
                    values_from = percent) %>%
  waffle::waffle(legend_pos = "bottom", title = "Deaths Per School Shooting", 
       xlab="1 square ~ 1%")+  scale_fill_viridis_d()

'''

In the second column, we will include what are called value boxes to contain statistics that will remain static as the user moves through the tabs of the first column.

Column {data-width=30}
------------------------------------- 

We want to display some important statistics, such as:

  • Total number of people wounded in a school shooting
  • Total number of deaths from a school shooting
  • Median number of shots fired
  • Percentage of school shootings where the shooter was the only victim
  • Percentage of school shootings where a single handgun was used
  • Percentage of school shootings where the shooter was male

To create a value box we will use the valueBox() function from the flexdashboard package. The text for the the value box is specified by the text following the ### syntax.

There are a few arguments to be aware of for this function:

  1. value - this is the value to be displayed in the box - this usually a number, but might be text
  2. caption - if desired, you can add text to be displayed under the value but keep in mind that you will also include text with the ### syntax
  3. icon - if you would like to add an icon you can specify it like so: icon = fa-flag
  4. color - this changes the color of the box
  5. href - if you would like to add a URL link you can do so with this argument

We can create a value box for the total number of people wounded as follows, where we use the base sum() function to calculate the sum of all the values for the Wounded variable which was extracted using the pull() function from the dplyr package. We need to remove NA values to be able to calculate the sum and we can do this using the na.rm = TRUE argument.

Column {data-width=300}
------------------------------------- 


### **Total Wounded**
    
'''{r}
valueBox(value = sum(pull(shooting_data, Wounded), na.rm = TRUE),
         color = "white")
'''
    
### **Total Deaths**

'''{r}
valueBox(value = sum(pull(
  shooting_data,`Killed (includes shooter)`), na.rm = TRUE),
         color = "white")
'''

To calculate the percentage of school shootings where the shooter committed or attempted suicide, we will use our calculation which was explained in the [Data Analysis and Visualization] section. The paste0 function is used to add the percentage symbol.


### **Shooter committed or attempted suicide**

'''{r}

suicide <- (sum(pull(shooting_data,`Suicide (or attempted suicide) by Shooter (Y/N)`), na.rm = TRUE) /
            sum(pull(shooting_data, `Suicide (or attempted suicide) by Shooter (Y/N)`)>=0, na.rm = TRUE))*100
suicide <- round(suicide, 1)

reporting_suic <- (sum(pull(shooting_data, `Suicide (or attempted suicide) by Shooter (Y/N)`)>=0, na.rm = TRUE)/
              length(pull(shooting_data, `Suicide (or attempted suicide) by Shooter (Y/N)`)))*100
reporting_suic <- round(reporting_suic, 1)

valueBox(value = paste0(suicide,"%"), 
         color = "white")
'''

For the value box of the percentage of school shootings where a single handgun was used was calculated by using the case_when() function to specify all cases where the Firearm Type variable was equal to "Handgun" as TRUE and all others as FALSE. This allows us to use the base sum() function as TRUE values will be counted as a value of 1 and FALSE values will be counted as a value of 0. This sum was then divided by the total number of school shooting events by getting the length of the Firearm Type variable using the base length() function. The next value box about the gender of the shooter was calculated in a similar manner.

    
### **Use of a Single Handgun**

'''{r}

handgun <-paste(as.character(round(100 *(sum(case_when(
      pull(shooting_data,`Firearm Type`) == "Handgun" ~ TRUE,
                                                 TRUE ~ FALSE), na.rm = TRUE)
    /
      sum(pull(shooting_data, `Firearm Type`)>=0, na.rm = TRUE)),
    1)), "%")

reporting_gun <- (sum(pull(shooting_data, `Firearm Type`)>=0, na.rm = TRUE)/
              length(pull(shooting_data, `Firearm Type`)))*100
reporting_gun <- round(reporting_gun, 1)


valueBox(value = handgun,
  color = "white")

'''

### **Shooter Was Male**
'''{r}


gender <- paste(as.character(round(100 * (sum(
    case_when(pull(shooting_data,`Shooter Gender`) == "Male" ~ TRUE,
                                                        TRUE ~ FALSE),
                                      na.rm = TRUE)
    /
      sum(pull(shooting_data, `Shooter Gender`)>=0, na.rm = TRUE)),
    1)), "%")

reporting_male <- (sum(pull(shooting_data, `Shooter Gender`)>=0, na.rm = TRUE)/
              length(pull(shooting_data, `Shooter Gender`)))*100
reporting_male <- round(reporting_male, 1)


valueBox(value = paste(gender),
  color = "white")
'''

Additional text about the reporting rate for these statistics was added using the ### syntax. Additionally inline code is evaluated using the notation "r " Again notice that "'" was used instead of ""` just for illustrative purposes to allow this R Markdown document to render the code from the dashboard file.


###

reporting rate of shooter suicide = 'r reporting_suic'%,  
reporting rate of gun type = 'r reporting_gun'%,  
reporting rate of shooter gender = 'r reporting_male'%

The State Statistics Page (Interactive)


Let’s create a page for State Statistics we would like to share. Importantly this page allows for the user to choose what state to look at.

Look


This is what the page will look like:

Overall Structure


Here is the overall structure for this page:

Note: the other value Boxes are not included in this image. You can see that the renderPlot() function is used for plots and the renderValueBox() function is used for value boxes.

Details


On this page we want the user to be able to select data for a specific state and render plots and get statistics just for the selected state. To do this we will utilize the renderPlot() and renderValueBox() functions of the flexdashboard package, as well as the selectInput() function from the shiny package. See this website for more information on using shiny to create interactive dashboards with flexdashboard.


Click here to see the code for this page.

The first thing we need to do to allow this page to be interactive is to add runtime: shiny to the YAML header at the top of the R Markdown file.

The next thing we want to do is add the {.sidebar} attribute to the first column of this page. This allows us to use shiny input functions in this column.

Then, we use the selectInput() function to create a menu for the user to interact with and add it to this column.

Finally, we use the renderPlot() function and renderValueBox() function to use the input from the user to render plots and value boxes based on their input.

The selectInput() function allows us to provide the user with a pull down menu of options for states. The main arguments for this function are:

  1. inputId - this is what the selection will be called in subsequent code
  2. label - this is what the user sees above the pull down menu
  3. choices - this is a list of options for the menu
  4. selected - this causes a particular option to be the default choice

This is placed in a column on the far left side that is more narrow than the others.

State Statistics {data-icon=fa-flag-checkered}
===================================== 

Column {.sidebar data-width=250}
-----------------------------------------------------------------------

Note that the statistics shown do not account for other possibly influential state specific features like population density or gun laws among others.


'''{r}
  
selectInput(inputId = "state_selected", 
            label = "Select a state to explore:",
            choices = shooting_data %>% 
            pull(State) %>% 
            unique() %>%
            sort(), selected = "Alabama")

#  Washington, D.C. gets excluded by this
'''

Note that we used the unique() function to select only unique values of the State variable of the shooting_data tibble. The sort() function was used to put the options in alphabetical order.

In the next column, we have our plots like we did on the last page. Again we will use tabset. However, the difference here is that we need to include the renderPlot() function around all of our code for each plot and we need to use the data that the user selected.

This will automatically be in a data object called input and it will be within a variable called state_selected" based on what we used for the inputID in the select_Input() function (this requires the base R way of selecting a specific variable using the $).

Notice that the renderPlot() function requires that the code be within brackets {}. The data is filtered first for just the state that was selected. The code for the plots is essentially the same with minor modifications to allow for all unique cases that the different states present. For example the deaths_perc_event %<>%filter (!duplicated(category)) is added to the last plot about the number of deaths per school shooting to avoid duplication of the rows in cases like Colorado where the there is only one event that had 4 or more deaths (because in the other cases this value is a sum of all school shooting with 4 or more deaths).

It’s always good to check as many possible input values as possible to make sure that your plot shows up as you expect!

Column {data-width=750 .tabset .tabset-fade}
-----------------------------------------------------------------------

### Yearly Deaths and Shootings

'''{r}
renderPlot({
shooting_data_state <- shooting_data %>% filter(State == input$state_selected)

shootings_per_year<- shooting_data_state  %>%
    group_by(Date_year) %>%
    count() %>%
  rename("Shootings" = n) %>%
    ungroup()

deaths_per_year<-shooting_data_state  %>% 
  group_by(Date_year) %>%
  summarize(Deaths =sum(`Killed (includes shooter)`))


per_year <- full_join(shootings_per_year, deaths_per_year)
per_year %<>% pivot_longer(cols = (-Date_year), 
                           values_to = "events", 
                           names_to = "id")

per_year %<>% 
  mutate(id = forcats::fct_inorder(id))

per_year %<>%
    ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col()+
    facet_wrap(~id, scales = "free", 
               labeller = as_labeller(c(Shootings = "Shootings (# of events)", 
                                        Deaths = "Deaths (# of people)")), 
               strip.position = "left")+
    scale_x_continuous(breaks = seq(start, end, by = 5),
                 labels = seq(start, end, by = 5),
                 limits = c(start-1, end+1)) +
    theme_minimal() +
  scale_fill_manual(values = c("black", "black"))+
    labs(title = "Yearly Shootings and Deaths Attributable to School Shootings",
         subtitle = "United States",
         y = NULL,
         x = "Year") +
    theme_dashboard()+
    theme(title = element_text(size = 16, face = "bold"),
          axis.text = element_text(size = 14))
})
'''

### Yearly Cumulative Deaths and Shootings

'''{r}

renderPlot({

shooting_data_state <- shooting_data %>% filter(State == input$state_selected)

shootings_per_year<- shooting_data_state  %>%
    group_by(Date_year) %>%
    count() %>%
  rename("Shootings" = n) %>%
    ungroup()

shootings_per_year_cum <- 
  shootings_per_year %>%
  mutate(Shootings = cumsum(Shootings))

deaths_per_year<-shooting_data_state  %>% 
  group_by(Date_year) %>%
  summarize(Deaths =sum(`Killed (includes shooter)`))

deaths_per_year_cum <- 
  deaths_per_year %>%
  mutate(Deaths = cumsum(Deaths))

per_year_cum <- full_join(shootings_per_year_cum, deaths_per_year_cum)


per_year_cum %<>% 
  pivot_longer(cols = c(Shootings, Deaths ), 
               values_to = "events", 
                names_to = "id")
                
per_year_cum %<>% 
  mutate(id = forcats::fct_inorder(id))

per_year_cum %>%
ggplot(aes(x = Date_year, y = events, fill =id)) +
    geom_col()+
    facet_grid(~id)+
    scale_x_continuous(breaks = seq(start, end, by = 5),
                 labels = seq(start, end, by = 5),
                 limits = c(start-1, end+1)) +
    scale_fill_manual(values = c("black", "black"))+
    theme_minimal() +
    labs(title = "Cumulative Yearly Shootings and Deaths\nAttributable to School Shootings",
         subtitle = input$state_selected,
         y = "Cumulative number of events",
         x = "Year") +
    theme(legend.position = "none", 
        legend.title = element_blank(),
        axis.text.x = element_text(angle = 90),
        strip.background = element_rect(fill="cornflowerblue"),
        strip.text = element_text(colour = 'white', face = "bold", size = 14))

})

'''

### Deaths Per Shooting

'''{r}

renderPlot({

shooting_data_state <- shooting_data %>% filter(State == input$state_selected)
library(tidyr)
deaths_perc_event <-shooting_data_state %>%
   count(`Killed (includes shooter)`) %>%
   rename("num_events"= n) %>%
     tidyr::drop_na() %>%
   mutate(percent = round(num_events/sum(num_events)*100, digits =1))

greater_than4 <- 
  deaths_perc_event %>% 
  filter(`Killed (includes shooter)` >= 4) %>% 
  colSums()

deaths_perc_event %<>% bind_rows(greater_than4)

deaths_perc_event %<>% 
  mutate(category = paste0(`Killed (includes shooter)`, " deaths ", "\n(", percent, "%)")) 

deaths_perc_event %<>% 
  mutate(category = case_when(
    category ==  last(pull(deaths_perc_event, category)) ~ paste0("4+ deaths ", "\n(", percent, "%)"),
    category == "1 deaths" ~ "1 death",
    TRUE ~ category))

deaths_perc_event %<>% 
  filter (!duplicated(category))

deaths_perc_event %>% 
  select(-`Killed (includes shooter)`) %>%
  filter(str_detect(category, "0 deaths|1 death|2 deaths|3 deaths|4\\+")) %>%
  mutate(percent = round(percent)) %>%
  select(-num_events) %>%
  tidyr::pivot_wider(names_from = category, 
                    values_from = percent) %>%
  waffle::waffle(legend_pos = "bottom", title = "Deaths Per School Shooting", 
       xlab="1 square ~ 1%")+  scale_fill_viridis_d()

})

'''

In the third column, the state specific statistics are displayed. Some of these are static, while others update for the state selected. To calculate some of these we will also use data form the poliscidata function to get the state population values in 2010. The pop2010_hun_thou variable is the population in terms of 100,000 people.

Column {data-width=450}
-----------------------------------------------------------------------

### **Total State Deaths**

'''{r}
renderValueBox({
shooting_data_state <- shooting_data %>% filter(State == input$state_selected)


valueBox(sum(pull(shooting_data_state,`Killed (includes shooter)`), na.rm = TRUE),
         color = "white")
})
'''

### **US State Average Death Count**

'''{r}
shooting_data_state <-shooting_data %>% 
  group_by(State_abb, State) %>%
  count(na.rm = TRUE) %>%
  rename(shootings = n) %>%
  ungroup() %>%
  mutate(state_sum = sum(shootings)) %>%
  mutate(state_avg = state_sum/50)

state_data <- poliscidata::states
state_data %<>%
  select(stateid, pop2010, pop2010_hun_thou) %>%
  mutate(stateid = as.character(stateid))%>%
  mutate(stateid = str_remove_all(stateid, pattern = " "))

shooting_data_state<-left_join(shooting_data_state, state_data, by = c("State_abb" = "stateid"))

deaths_State <-shooting_data %>% 
  group_by(State) %>%
  summarize( deaths = sum(`Killed (includes shooter)`, na.rm = TRUE))

state_data <- left_join(shooting_data_state, deaths_State)

USavg <- round(mean(pull(state_data, deaths), na.rm = TRUE), 2)
valueBox(USavg, color = "white")
'''


### **State Death Rate (per 100,000 people)**

'''{r}
state_data %<>%
  mutate(percapita_deaths  = deaths/pop2010_hun_thou)

renderValueBox({
  
  shooting_data_state <- state_data %>% filter(State == input$state_selected)

  valueBox(format(round(pull(shooting_data_state, percapita_deaths), digits = 3), nsmall = 3),
         color = "white")
})


'''

### **US National Death Rate (per 100,000 people)**

'''{r}
renderValueBox({

 US_percap <-summarize(state_data, sum(deaths, na.rm = TRUE))/ (summarize(state_data,sum(pop2010, na.rm = TRUE)) /100000)
 
valueBox(value = round(US_percap, digits = 3),
         color = "white")
})
'''

### **State Shooting Rate (per 100,000 people)**

'''{r}

state_data %<>%
  mutate(percapita_shootings  = shootings/pop2010_hun_thou)

renderValueBox({
  
  shooting_data_state <- state_data %>% filter(State == input$state_selected)

valueBox(format(round(pull(shooting_data_state, percapita_shootings), digits = 3), nsmall = 3),
         color = "white")
})


'''

### **US National Shooting Rate (per 100,000 people)**

'''{r}
renderValueBox({

 US_percap <-summarize(state_data, sum(shootings, na.rm = TRUE))/ (summarize(state_data,sum(pop2010, na.rm = TRUE)) /100000)
 
valueBox(value = round(US_percap, digits = 3),
         color = "white")
})
'''

###

Per capita calculations are based on 2010 population values.

The Map page (Interactive)


Next, we create our map page. Previously, in the Data Exploration and Wrangling section, we geocoded our data and modified the latitude and longitude variables so that events that occurred in the same location would have slightly different values so that they will not cover one another in our map.

To create our map, we will use the leaflet package which uses the Leaflet JavaScript library.

Leaflet


Leaflet works by provided by adding base data (such as a map) and then adding markers if desired in layers. This is very similar to how ggplot2 functions (pun intended).

The layers displayed can be controlled using a sort of legend. Depending on the type of layers, some information may be displayed mutually exclusive of the other layers; other layers (such as circles/general markers) can be toggled on and off.

Clustering options can also be applied to circles/markers. Some examples of this can be found on the bottom of this website.

The groups in leaflet can be thought of as layer-specific IDs that create labels for legends and allow specific layers to be referred to in separate functions.

Thus, if we called a group “Layer 1” and then in a subsequent layer refer to “Layer 1”, leaflet will correctly identify which layer is being referenced.

Note that leaflet can require a lot of computational power depending on the types of maps produced.

Look


This is what the page will look like:

Overall Structure


The overall structure for this page is simple. There is just one column ,which will contain the map.

Details



Click here to see the code for this page.

First, we create a smaller dataset that just includes the data that we want to use in the map. We will include the date, the name of the school and the narrative for each point as a popup that will be shown when the user hovers over a point.

We need to do this using HTML code as the leaflet package will ultimately render the map using this language.

We use the paste() function to combine these elements as well as HTML code to create line breaks and bold the name of the school.

To create line breaks in HTML, the <br> syntax is used. This is used to separate each part of the elements that are getting pasted together with the base paste() function by being specified as the separator with the sep argument.

To create bold font in HTML, the text is surrounded by <b> and </b> like so: <b> Bold text </b>. Thus only the school name is in bold.

Finally, the <div> and </div> are content dividers in HTML. They separate the individual school shooting event information sections that will be plotted on the map. The first divider can also take information about the style of the output. This uses CSS code, which is what is used to stylize HTML.

The code here states that the height of the text box for each event should have a height that is proportional to the text, that the height of each line should be of 1 em units (em stands for element). Hence, 1 unit relative to the size of the element. Therefore gaps between lines are the same height as the lines of text. The overflow:visible code specifies what to do in case the text box text is too large - in this case users can scroll (see here for more options), and the padding specification sets the size of the margins of the text box around the text.

See this website to learn more about HTML code.

Map {data-icon="fa-map"}
===================================== 
Column
------------------------------------- 
    
### 

This map shows where school shootings took place in the United States between January 1970 to June 2020 according to the the open-source [Center for Homeland Defense and Security](https://www.chds.us/c/) (CHDS) [K-12 School Shooting Database](https://www.chds.us/ssdb/dataset/). Click the circles for more information.

    
'''{r}
# specify the popups

shooting_information0 <- paste('<div style="height:auto;line-height:1em;overflow:scroll;padding:1em">',
                              shooting_data_geocoded$Date,
                              "<b>",
                              shooting_data_geocoded$School,
                              "</b>",
                              shooting_data_geocoded$`Narrative (Detailed Summary/ Background)`,
                              "</div>",
                              sep = "<br>")


'''

The next bit of code then uses this data and the shooting_data_geocded to actually create the map!

The leaflet() function from the leaflet package creates a Leaflet map widget using the htmlwidgets package, which allows the map to be rendered as an application within HTML websites.

This first line of code starts the process of making the widget, but just like the ggplot() function from ggplot2 it creates an empty map and layers need to be added.

The addProviderTiles() function from the leaflet() package does just that, by adding the map background. We will add three different kinds of map backgrounds. See here for all the options of providers which create a variety of distinct backgrounds and then the group argument names each of these layers to be referred to later. The last layer added will be the one shown by default.

At this point we still only have a map in general. Now we need to add the data about school shooting events.

To do this, we add markers to the plot using the addCircleMarkers() function. This function takes many different arguments. See details about them here.

Importantly, we need to specify what variables in our provided data shooting_data_geocoded contains the longitude values (lng) and the latitude values (lat).

We will also use the following arguments:

  • radius - argument specifies how large the circles for the points will be
  • color - argument specifies the color of the individual points
  • fillOpacity - argument allows for the filling of the points to a bit translucent if set below 1
  • clusterOptions - argument can be used to cluster points together into larger circles
  • group - argument specifies what the points should be called in the legend and what this layer should be referred to as for later use

We also add a mini map using the addMiniMap() function, which can be useful to see where you are on the map. The type of plot style to use for the mini map is specified with the tiles argument and the toggleDisplay argument allows for the user to remove this feature.

Importantly, the addLayersControl() function allows users to toggle between different backgrounds and markers. In our case we have three different background layers which are referred to as baseGroups and we have one overlayGroups which is our circle markers for school shooting events. The group names for these need to be identified to allow users to toggle between them.

The set_view() function allows for the starting position and zoom to be modified. This allows us to center the map around the continental US.

'''{r}
leaflet(shooting_data_for_map) %>%
  addProviderTiles(provider = providers$OpenStreetMap, group = "OpenStreetMap") %>%
  addProviderTiles(provider = providers$Esri.WorldImagery, group = "ESRI World Imagery") %>%
  addProviderTiles(provider = providers$Stamen.TonerLite, group = "Toner")%>%
  addCircleMarkers(popup = ~shooting_information0,
                     lng = ~longitude,
                     lat = ~latitude,
     radius = 5,
     color = "red",
     fillOpacity = 0.2,
     clusterOptions = markerClusterOptions(),
     group = "Circles") %>%
  addMiniMap(tiles = providers$Stamen.Toner,
              toggleDisplay = TRUE) %>%
  addLayersControl(
     baseGroups = c("Toner Lite",
                    "OpenStreetMap",
                    "ESRI World Imagery"),
     overlayGroups = c("Circles")) %>%
   setView(lng = -98.35, lat = 39.5, zoom = 4)
'''

The Tutorial Page


Here, we create a Tutorial page that links to this case study. This provides a simple overview of how we created the dashboard.

Look


This is what the page will look like:

Overall Structure


To create this page we will use a special layout called a storyboard. Story boards are used in many other fields, but the idea is that there are multiple images in a sequence. To create our storyboard page with flexdashboard we will use {.stroyboard} next to the page name. Each page name will be specified using this syntax: ###.

Here you see the top part of the overall structure:

Details


The code for this page is similar to the other pages, except for the story board structure.


Click here to see the code for this page.
Tutorial {.storyboard data-icon="fa-list-ol"}
=========================================   

### **1)** Load the `flexdashboard` package.

Install the package (and other supporting optional packages) if you don't have them installed already.

'''{r, echo=TRUE, eval=FALSE}
install.packages("flexdashboard")
install.packages("shiny")
install.packages("leaflet")
install.packages("ggplot2")
'''

Once installed, load the package(s) into the `R` environment.

'''{r, echo=TRUE}
library(flexdashboard)
library(shiny)
library(leaflet)
library(ggplot2)
'''

This all needs to be done separately in the `R` console.

### **2)** Create an `RMD` document.   

Dashboards can be created with `flexdashboard` in the `HTML` format. 

The`flexdashboard` package uses `RMarkdown` to produce dashboards that can contain `R` output.

This makes it possible to include several mediums in dashboards such as plots created with `ggplot2` or maps created with `leaflet`.
    
### **3)** Create an appropriate `YAML`.

The use of `flexdashboard` alters the way R Markdown documents function. 

R Markdown documents can be rendered into many different outputs, one of which is a dashboard. 
The `YAML` header sets up how the document output should be created.

Here is an example of a `YAML` header that creates an `HTML` document from an R Markdown document:


---
title: "Untitled"
author: "John Smith"
date: "8/12/2020"
output: html_document
---


We used the following `YAML` for this dashboard, which importantly includes `flexdahsboard::flex_dashboard`which specifies that a dashboard should be created and `runtime:shiny` which allows for the dashboard to be interactive:

output: 
  flexdashboard::flex_dashboard:
    logo: https://icons.iconarchive.com/icons/icons8/windows-8/48/Programming-Dashboard-icon.png
    theme: readable
    orientation: columns
    source_code: embed
    vertical_layout: fill
runtime: shiny


We also introduced an icon as a logo, provided a theme with a color scheme, defined the orientation (and thus order) of coded output, added a navigation bar item to give users easy access to the code used, and  limited scrolling with the `verticle_layout: fill` option.

### **4)** Design the layout of the dashboard.

Dashboards are inherently visual, making this step the most time intensive after content creation. To goal is to present the data in a way that is both meaningful and visually appealing.

On this dashboard, we wanted to present static plots of the United States and of individual states. We also wanted to display the locations of school shootings and provide some information about school shootings. Aside from being a dashboard, we wanted to create an educational resource that was reproducible for others. Lastly, as this is a sensitive topic, we wanted to raise awareness and provide information that could help others act.

Given these goals, we decided on the following page layout:

+ About
+ The Data
+ US Statistics
+ State Statistics
+ Map
+ Tutorial
+ Get Help

The first page gives users to the opportunity to look at the data themselves. More complicated components such as the map of each incident were left alone on a single page. US and state-level statistics were separated from one another. This short tutorial on how to create the dashboard and source code were included in the dashboard with programmers at all levels in mind.

### **5)** Add content to the dashboard.

You can begin adding content to the dashboard once you have an initial layout in mind. Keep in mind that this will likely be an iterative process. 

The R Markdown file used to create a dashboard with `flexdashboard` works similarly as it does in other cases, with a few exceptions.

R code chunks can be defined like so:


'''{r, echo = TRUE}
# Code chunks can be explicitly included
'''

'''{r, echo = FALSE}

# Code chunks are hidden by default 
'''

Pages and columns within pages can be defined like so:


Page
=========================================   

Column {data-width=500}
-------------------------------------

Column {data-width=500}
-------------------------------------


### **6)** Add content to the pages and columns.

Plots and other elements can be added within columns like so:

### Plot name

'''{r}
# include plot code here
'''

Value Boxes, which are essentially text boxes, can be defined like so:


### ValueBoxText

'''{r}
valueBox(value = 10
  color = "white")



Gauges, can be defined like so:

### GaugeText

'''{r}
flexdashboard::gauge(value = 10, 
                       min = 0, 
                       max = 100, 
                    symbol = "%")

'''

####
Which will produce output like this:
'''{r, out.width= "40%", echo = FALSE}
knitr::include_graphics(here::here("img", "gauge_output.png"))
'''

### Additional Info
As mentioned before, the `flexdashboard` metadata included in the `YAML` also alters how R Markdown documents are rendered. For more on how you can leverage both the `RMarkdown` package and the `flexdashboard` package to produce a dashboard, click [here](https://rmarkdown.rstudio.com/flexdashboard/index.html).

This [supplementary resource by the Open Case Studies project](https://opencasestudies.github.io/ocs-bp-school-shootings-dashboard/) provides a case study on how to create this very dashboard in more detail.

The Get Help page


We create a Get Help page to spread awareness on this important public health topic.

Look


This is what the page will look like:

Overall Structure


Details


This page has two columns. The first column is much wider than the second. This first column includes two colored backgrounds which were created using CSS code. See [The About Page] Details section to for more details about how this works.

The text that we want altered with this particular style is delineated by the
to start and the

to end the style.

The instructions for the style are within the

content dividers.

Inside these dividers is CSS code, which is what is used to stylize HTML. The div.blue is the name of the first particular style which involves a particular background color (#e6f0ffF) (see here for more color options), with a boarder radius of 5 pixels to round the edges of the background color around the text with a size 5 pixel radius. The code also states that a padding specification for the size of the margins of the text box around the text and it specifies that font should be of 20 pixels.

The div.blue specifies that blue is the name of this style, thus we can then use

(called a CSS selector) to style the text this way. This can then be used again any time we want this style like so:

text

See this website to learn more about HTML and CSS.

We also see a list on this page, + signs are used to indicate new items. Importantly two spaces are necessary after each item to start a new line.

The other unique aspect about this page are the telephone links like so [+1-844-5-SAYNOW](tel:18445729669).

By using tel: and the number, users can click this link to directly call the telephone number from their computer or phone if their device has such capabilities.


Click here to see the code for this page.
Get Help {data-icon="fa-exclamation-triangle"}
=========================================   

Column {data-width=800}
-------------------------------------

###

**Warning Signs**

From [Sandy Hook Promise](https://www.sandyhookpromise.org/gun-violence/know-the-signs-of-gun-violence/)...

<style>
div.blue { background-color:#e6f0ff; border-radius: 5px; padding: 20px;}
</style>
<div class = "blue">

Here is a list of potential warning signs that can signal an individual may be in crisis and/or need help:

+ Suddenly withdrawing from people and activities
+ Consistent bullying or intimidating others, or being bullied by others
+ Extreme mood or personality changes
+ Victim of constant social rejection
+ Talking about plans or actively making plans to harm themselves or others
+ Bringing a weapon to school – or threatening or talking about doing so
+ Bragging about or warning others about an upcoming act of violence
+ Recruiting others to join in a planned act of violence
+ Warning students to stay away from school or events
+ Expressing fascination with guns and/or school shootings
+ Expressing hopelessness about the future
+ Extreme, prolonged sadness or distress
+ Expressing or showing feelings of isolation
+ Bragging about access to guns

**This list is not a comprehensive list of warning signs nor does exhibiting one of these signs indicate imminent violence.**

According to the following article:

Flannery, D. J., Modzeleski, W. & Kretschmar, J. M. Violence and School Shootings. Curr Psychiatry Rep 15, 331 (2013). DOI: [10.1007/s11920-012-0331-6](https://doi.org/10.1007/s11920-012-0331-6)

"To date, studies of school shootings have concluded that no
consistent and reliable profile of school shooters exist, and
most researchers and clinicians would agree that predicting
violent behavior is a slippery slope that will usually result in
more false positives than false negatives."

"...most shooters were depressed, had experienced some significant
loss, felt persecuted or bullied by others, and had prior
difficulty coping or had previously tried suicide. Most of
the shooters did not, however, have a history of drug abuse
or violence or cruelty to animals, common psychiatric indicators of risk, nor did they report excessive exposure to
violence in the media (though many produced their own
violent themes in writings or drawings)."

</div>


<style>
div.red { background-color:#BC8F8F; border-radius: 5px; padding: 20px;}
</style>
<div class = "red">

According to the [National Institute of Mental Health (NIMH)](https://www.nimh.nih.gov/health/publications/teen-depression/index.shtml){target="_blank"}:

For youths who may be at risk for suicidal behavior, visit the **National Suicide Prevention Lifeline (NSPL)** website at [www.suicidepreventionlifeline.org](www.suicidepreventionlifeline.org){target="_blank"}.

Additionally, the **Crisis Text Line** is another free, confidential resource available 24 hours a day, seven days a week. Visit [www.crisistextline.org](www.crisistextline.org){target="_blank"} for more information.

Also see [here](https://www.mhanational.org/depression-teens-0){target="_blank"} for more information about how to recognize and help youths experiencing symptoms of depression and warning signs of suicide.

</div>


Column {data-width=200}
-------------------------------------

### 

**Respond to Warning Signs**

When concerned about troubling behaviors, tell a trusted adult.


Call **911** if you feel there is an immediate threat. 

Call [+1-844-5-SAYNOW](tel:18445729669) if you would like to submit an anonymous safety concern.

Text “HOME” to **741741** to text a trained crisis counselor 24 hours a day.

The **National Suicide Prevention Lifeline (NSPL)** is available 24 hours a day, every day at **[1-800-273-TALK (8255)](tel:18002738255)**. 

The deaf and hard of hearing can contact the **(NSPL)** via TTY at **[1-800-799-4889](tel:18007994889)**. All calls are confidential.

Summary


Synopsis


In this case study, we demonstrated the basics of R Markdown and how to create a dashboard with using the flexdashboard package. We also demonstrated how to include an interactive table with the DT package, how to include interactive plots using functions of the shiny package such as renderPlot(). We included interactive value boxes using the renderValueBox() function from the flexdashboard package, which works with the shiny package. Finally, we showed how to include interactive maps using the leaflet package.

This case study also explored how to properly calculate and interpret percentages when the data has missing values. We also discussed the benefits and limiting aspects of pie charts (using the ggplot2 package) and waffle plots (using the waffle package).

Overall, the dashboard we created which can be found here, shows that the number of school shootings per year has increased overtime. Further investigation is necessary to determine if this is simply due to increases in population alone or if the rate has increased due to other factors and if so, what those factors might be. It is also clear that the number of school shootings and the number of deaths per capita varies by state. There appears to be other aspects accounting for state differences.

Note the limitations of the dashboard in the Limitations section.

Suggested Homework


Create another dashboard with graphs and statistics featuring other elements within this dataset. For example, students may create graphs that explore what school events are reported to have more school shootings.

Additional Information


Session Info


sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] OCSdata_1.0.2             vembedr_0.1.5            
 [3] maps_3.4.0                leaflet_2.0.4.1          
 [5] shiny_1.7.1               flexdashboard_0.5.2      
 [7] poliscidata_2.3.0         waffle_0.7.0             
 [9] ggforce_0.3.3             forcats_0.5.1            
[11] htmltools_0.5.2           DT_0.20                  
[13] lubridate_1.8.0           sf_1.0-5                 
[15] ggmap_3.0.0               ggplot2_3.3.5            
[17] tidyr_1.1.4               stringr_1.4.0            
[19] dplyr_1.0.7               tibble_3.1.6             
[21] googlesheets4_1.0.0       readr_2.1.1              
[23] koRpus.lang.en_0.1-4      koRpus_0.13-8            
[25] sylly_0.1-6               read.so_0.1.1            
[27] wordcountaddin_0.3.0.9000 magrittr_2.0.2           
[29] knitr_1.37                here_1.0.1               

loaded via a namespace (and not attached):
  [1] backports_1.4.1     Hmisc_4.6-0         plyr_1.8.6         
  [4] sp_1.4-6            splines_4.1.2       crosstalk_1.2.0    
  [7] usethis_2.1.5       digest_0.6.29       gdata_2.18.0       
 [10] fansi_1.0.2         checkmate_2.0.0     cluster_2.1.2      
 [13] tzdb_0.2.0          remotes_2.4.2       extrafont_0.17     
 [16] vroom_1.5.7         extrafontdb_1.0     jpeg_0.1-9         
 [19] colorspace_2.0-2    mitools_2.4         xfun_0.29          
 [22] crayon_1.5.0        jsonlite_1.8.0      lme4_1.1-27.1      
 [25] survival_3.2-13     glue_1.6.1          polyclip_1.10-0    
 [28] gtable_0.3.0        gargle_1.2.0        car_3.0-12         
 [31] weights_1.0.4       Rttf2pt1_1.3.9      abind_1.4-5        
 [34] scales_1.1.1        DBI_1.1.2           Rcpp_1.0.8         
 [37] plotrix_3.8-2       viridisLite_0.4.0   xtable_1.8-4       
 [40] htmlTable_2.4.0     units_0.7-2         bit_4.0.4          
 [43] foreign_0.8-81      proxy_0.4-26        Formula_1.2-4      
 [46] survey_4.1-1        htmlwidgets_1.5.4   descr_1.1.5        
 [49] httr_1.4.2          gplots_3.1.1        RColorBrewer_1.1-2 
 [52] ellipsis_0.3.2      mice_3.14.0         pkgconfig_2.0.3    
 [55] farver_2.1.0        nnet_7.3-16         sass_0.4.0         
 [58] utf8_1.2.2          labeling_0.4.2      tidyselect_1.1.2   
 [61] rlang_1.0.1         later_1.3.0         munsell_0.5.0      
 [64] cellranger_1.1.0    tools_4.1.2         cli_3.2.0          
 [67] generics_0.1.1      broom_0.7.11        evaluate_0.15      
 [70] fastmap_1.1.0       yaml_2.3.5          bit64_4.0.5        
 [73] fs_1.5.2            caTools_1.18.2      purrr_0.3.4        
 [76] RgoogleMaps_1.4.5.3 nlme_3.1-153        mime_0.12          
 [79] compiler_4.1.2      rstudioapi_0.13     curl_4.3.2         
 [82] png_0.1-7           e1071_1.7-9         tweenr_1.0.2       
 [85] bslib_0.3.1         stringi_1.7.6       highr_0.9          
 [88] lattice_0.20-45     Matrix_1.3-4        classInt_0.4-3     
 [91] nloptr_1.2.2.3      vctrs_0.3.8         pillar_1.7.0       
 [94] lifecycle_1.0.1     jquerylib_0.1.4     data.table_1.14.2  
 [97] bitops_1.0-7        httpuv_1.6.5        sylly.en_0.1-3     
[100] R6_2.5.1            latticeExtra_0.6-29 promises_1.2.0.1   
[103] KernSmooth_2.23-20  gridExtra_2.3       boot_1.3-28        
[106] MASS_7.3-54         gtools_3.9.2        assertthat_0.2.1   
[109] rprojroot_2.0.2     rjson_0.2.21        withr_2.5.0        
[112] parallel_4.1.2      hms_1.1.1           grid_4.1.2         
[115] rpart_4.1-15        class_7.3-19        minqa_1.2.4        
[118] rmarkdown_2.11      carData_3.0-5       googledrive_2.0.0  
[121] base64enc_0.1-3    

Estimate of RMarkdown Compilation Time:

About 29 - 39 seconds

This compilation time was measured on a PC machine operating on Windows 10. This range should only be used as an estimate as compilation time will vary with different machines and operating systems.

Acknowledgments


We would like to acknowledge Elizabeth Stuart for assisting in framing the major direction of the case study.

We would like to acknowledge Michael Breshock for his contributions to this case study and developing the OCSdata package.

We would also like to acknowledge the Bloomberg American Health Initiative for funding this work.

LS0tDQp0aXRsZTogIk9wZW4gQ2FzZSBTdHVkaWVzOiBTY2hvb2wgU2hvb3RpbmdzIGluIHRoZSBVbml0ZWQgU3RhdGVzIg0KY3NzOiBzdHlsZS5jc3MNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBpbmNsdWRlczoNCiAgICAgICBpbl9oZWFkZXI6IEdBX1NjcmlwdC5SaHRtbA0KICAgIHNlbGZfY29udGFpbmVkOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCg0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9pbWcvaWNvbi1iYWhpLnBuZyIpOw0KICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47DQogIHBhZGRpbmctdG9wOiAyNDBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KDQo8IS0tIE9wZW4gYWxsIGxpbmtzIGluIG5ldyB0YWItLT4gIA0KPGJhc2UgdGFyZ2V0PSJfYmxhbmsiLz4gIA0KDQo8ZGl2IGlkPSJnb29nbGVfdHJhbnNsYXRlX2VsZW1lbnQiPjwvZGl2Pg0KDQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPScvL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV9hL2VsZW1lbnQuanM/Y2I9Z29vZ2xlVHJhbnNsYXRlRWxlbWVudEluaXQnPjwvc2NyaXB0Pg0KDQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+DQpmdW5jdGlvbiBnb29nbGVUcmFuc2xhdGVFbGVtZW50SW5pdCgpIHsNCiAgbmV3IGdvb2dsZS50cmFuc2xhdGUuVHJhbnNsYXRlRWxlbWVudCh7cGFnZUxhbmd1YWdlOiAnZW4nfSwgJ2dvb2dsZV90cmFuc2xhdGVfZWxlbWVudCcpOw0KfQ0KPC9zY3JpcHQ+DQoNCg0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBUUlVFLCBjb21tZW50ID0gTkEsIGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBvdXQud2lkdGggPSAnOTAlJykNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImJlbm1hcndpY2svd29yZGNvdW50YWRkaW4iLCB0eXBlID0gInNvdXJjZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQpyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiYWxpc3RhaXJlNDcvcmVhZC5zbyIpDQpsaWJyYXJ5KHdvcmRjb3VudGFkZGluKQ0KbGlicmFyeShyZWFkLnNvKQ0KDQpybWFya2Rvd246OjpwZXJmX3RpbWVyX3Jlc2V0X2FsbCgpDQpybWFya2Rvd246OjpwZXJmX3RpbWVyX3N0YXJ0KCJyZW5kZXIiKQ0KYGBgDQoNCiMjIyMgey5vdXRsaW5lIH0NCg0KYGBge3IsIGVjaG89RkFMU0UsIGZpZy5saW5rID0gImh0dHBzOi8vcnNjb25uZWN0LmJpb3N0YXQuamhzcGguZWR1L29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC8ifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgiaW1nIiwgImFib3V0cGFnZWxvb2sucG5nIikpDQpgYGANCg0KIyMjIw0KDQpUaGUgbGluayB0byB0aGUgZGFzaGJvYXJkIGRlc2NyaWJlZCBpbiB0aGlzIGNhc2Ugc3R1ZHkgaXMgW2hlcmVdKGh0dHBzOi8vcnNjb25uZWN0LmJpb3N0YXQuamhzcGguZWR1L29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC8pLiAgDQoNClRvIGFjY2VzcyB0aGUgR2l0SHViIFJlcG9zaXRvcnkgZm9yIHRoaXMgY2FzZSBzdHVkeSBzZWUgaGVyZTogaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtc2Nob29sLXNob290aW5ncy1kYXNoYm9hcmQvLg0KDQpZb3UgbWF5IGFsc28gYWNjZXNzIGFuZCBkb3dubG9hZCB0aGUgZGF0YSB1c2luZyBvdXIgYE9DU2RhdGFgIHBhY2thZ2UuIFRvIGxlYXJuIG1vcmUgYWJvdXQgdGhpcyBwYWNrYWdlIGluY2x1ZGluZyBleGFtcGxlcywgc2VlIHRoaXMgW2xpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvT0NTZGF0YSkuIEhlcmUgaXMgaG93IHlvdSB3b3VsZCBpbnN0YWxsIHRoaXMgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmBgYA0KDQpUaGlzIGNhc2Ugc3R1ZHkgaXMgcGFydCBvZiBhIHNlcmllcyBvZiBwdWJsaWMgaGVhbHRoIGNhc2Ugc3R1ZGllcyBmb3IgdGhlIFtCbG9vbWJlcmcgQW1lcmljYW4gSGVhbHRoIEluaXRpYXRpdmVdKGh0dHBzOi8vYW1lcmljYW5oZWFsdGguamh1LmVkdS9vcGVuLWNhc2Utc3R1ZGllcykuDQoNCkZvciB1c2VycyBvciBpbnN0cnVjdG9ycyB3aG8gb25seSB3aXNoIHRvIGxvb2sgYXQgdGhlIGJhc2ljcyBvZiBob3cgdG8gY3JlYXRlIGEgZGFzaGJvYXJkIGluIFIgd2l0aCB0aGUgYGZsZXhkYXNoYm9hcmRgIHBhY2thZ2UsIHBsZWFzZSBzZWUgdGhlIFsqKkRhc2hib2FyZCBCYXNpY3MqKl0gU2VjdGlvbi4gDQoNCiMjIyMgey5kaXNjbGFpbWVyX2Jsb2NrfQ0KDQoqKkRpc2NsYWltZXIqKjogVGhlIHB1cnBvc2Ugb2YgdGhlIFtPcGVuIENhc2UgU3R1ZGllc10oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvKXt0YXJnZXQ9Il9ibGFuayJ9IHByb2plY3QgaXMgKip0byBkZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIHZhcmlvdXMgZGF0YSBzY2llbmNlIG1ldGhvZHMsIHRvb2xzLCBhbmQgc29mdHdhcmUgaW4gdGhlIGNvbnRleHQgb2YgbWVzc3ksIHJlYWwtd29ybGQgZGF0YSoqLiBBIGdpdmVuIGNhc2Ugc3R1ZHkgZG9lcyBub3QgY292ZXIgYWxsIGFzcGVjdHMgb2YgdGhlIHJlc2VhcmNoIHByb2Nlc3MsIGlzIG5vdCBjbGFpbWluZyB0byBiZSB0aGUgbW9zdCBhcHByb3ByaWF0ZSB3YXkgdG8gYW5hbHl6ZSBhIGdpdmVuIGRhdGEgc2V0LCBhbmQgc2hvdWxkIG5vdCBiZSB1c2VkIGluIHRoZSBjb250ZXh0IG9mIG1ha2luZyBwb2xpY3kgZGVjaXNpb25zIHdpdGhvdXQgZXh0ZXJuYWwgY29uc3VsdGF0aW9uIGZyb20gc2NpZW50aWZpYyBleHBlcnRzLiANCg0KIyMjIw0KDQojIyMjIHsubGljZW5zZV9ibG9ja30NCg0KVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmNpYWwgMy4wIFsoQ0MgQlktTkMgMy4wKV0oaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLzMuMC91cy8pe3RhcmdldD0iX2JsYW5rIn0gVW5pdGVkIFN0YXRlcyBMaWNlbnNlLg0KDQojIyMjDQoNCiMjIyMgey5yZWZlcmVuY2VfYmxvY2t9DQoNClRvIGNpdGUgdGhpcyBjYXNlIHN0dWR5IHBsZWFzZSB1c2U6DQoNCldyaWdodCwgQ2FycmllIGFuZCBPbnRpdmVyb3MsIE1pY2hhZWwsIGFuZCBNZW5nLCBRaWVyIGFuZCBKYWdlciwgTGVhaCBhbmQgVGF1YiwgTWFyZ2FyZXQgYW5kIEhpY2tzLCBTdGVwaGFuaWUuICgyMDIwKS4gW2h0dHBzOi8vZ2l0aHViLmNvbS8vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZF0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkKS4gT3BlbiBDYXNlIFN0dWRpZXM6IFNjaG9vbCBTaG9vdGluZ3MgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgKFZlcnNpb24gdjEuMC4wKS4NCg0KIyMjIw0KDQpUaGUgdG90YWwgcmVhZGluZyB0aW1lIGZvciB0aGlzIGNhc2Ugc3R1ZHkgaXMgY2FsY3VsYXRlZCB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKSBhbmQgc2hvd24gYmVsb3c6IA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnJlYWR0YWJsZSA9IHRleHRfc3RhdHMoImluZGV4LlJtZCIpICMgcHJvZHVjaW5nIHJlYWRpbmcgdGltZSBtYXJrZG93biB0YWJsZQ0KcmVhZHRpbWUgPSByZWFkLnNvOjpyZWFkLm1kKHJlYWR0YWJsZSkgJT4lIGRwbHlyOjpzZWxlY3QoTWV0aG9kLCBrb1JwdXMpICU+JSAjIHJlYWRpbmcgdGFibGUgaW50byBkYXRhZnJhbWUsIHNlbGVjdGluZyByZWxldmFudCBmYWN0b3JzDQogIGRwbHlyOjpmaWx0ZXIoTWV0aG9kID09ICJSZWFkaW5nIHRpbWUiKSAlPiUgIyBkcm9wcGluZyB1bm5lY2Vzc2FyeSByb3dzDQogIGRwbHlyOjptdXRhdGUoa29ScHVzID0gcGFzdGUocm91bmQoYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfc3BsaXQoa29ScHVzLCAiICIpW1sxXV1bMV0pKSwgIm1pbnV0ZXMiKSkgJT4lICMgcm91bmRpbmcgcmVhZGluZyB0aW1lIGVzdGltYXRlDQogIGRwbHlyOjptdXRhdGUoTWV0aG9kID0gImtvUnB1cyIpICU+JSBkcGx5cjo6cmVsb2NhdGUoa29ScHVzLCAuYmVmb3JlID0gTWV0aG9kKSAlPiUgZHBseXI6OnJlbmFtZShgUmVhZGluZyBUaW1lYCA9IGtvUnB1cykgIyByZW9yZ2FuaXppbmcgdGFibGUNCmtuaXRyOjprYWJsZShyZWFkdGltZSwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNCioqUmVhZGFiaWxpdHkgU2NvcmU6ICoqDQoNCkEgcmVhZGFiaWxpdHkgaW5kZXggZXN0aW1hdGVzIHRoZSByZWFkaW5nIGRpZmZpY3VsdHkgbGV2ZWwgb2YgYSBwYXJ0aWN1bGFyIHRleHQuIEZsZXNjaC1LaW5jYWlkLCBGT1JDQVNULCBhbmQgU01PRyBhcmUgdGhyZWUgY29tbW9uIHJlYWRhYmlsaXR5IGluZGljZXMgdGhhdCB3ZXJlIGNhbGN1bGF0ZWQgZm9yIHRoaXMgY2FzZSBzdHVkeSB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKS4gVGhlc2UgaW5kaWNlcyBwcm92aWRlIGFuIGVzdGltYXRpb24gb2YgdGhlIG1pbmltdW0gcmVhZGluZyBsZXZlbCByZXF1aXJlZCB0byBjb21wcmVoZW5kIHRoaXMgY2FzZSBzdHVkeSBieSBncmFkZSBhbmQgYWdlLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpydCA9IHdvcmRjb3VudGFkZGluOjpyZWFkYWJpbGl0eSgiaW5kZXguUm1kIiwgcXVpZXQ9VFJVRSkgIyBwcm9kdWNpbmcgcmVhZGFiaWxpdHkgbWFya2Rvd24gdGFibGUNCmRmID0gcmVhZC5zbzo6cmVhZC5tZChydCkgJT4lIGRwbHlyOjpzZWxlY3QoaW5kZXgsIGdyYWRlLCBhZ2UpICU+JSAgIyByZWFkaW5nIHRhYmxlIGludG8gZGF0YWZyYW1lLCBzZWxlY3RpbmcgcmVsZXZhbnQgZmFjdG9ycw0KICB0aWR5cjo6ZHJvcF9uYSgpICU+JSBkcGx5cjo6bXV0YXRlKGdyYWRlID0gcm91bmQoYXMubnVtZXJpYyhncmFkZSkpLCAjIGRyb3BwaW5nIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcywgcm91bmRpbmcgYWdlIGFuZCBncmFkZSBjb2x1bW5zDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlID0gcm91bmQoYXMubnVtZXJpYyhhZ2UpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCmtuaXRyOjprYWJsZShkZiwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNClBsZWFzZSBoZWxwIHVzIGJ5IGZpbGxpbmcgb3V0IG91ciBzdXJ2ZXkuDQoNCjxkaXYgc3R5bGU9ImRpc3BsYXk6IGZsZXg7IGp1c3RpZnktY29udGVudDogY2VudGVyOyI+PGlmcmFtZSBzcmM9Imh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2Zvcm1zL2QvZS8xRkFJcFFMU2ZwTjRGTjNLRUxxQk5FZ2YyQXRwaTdXeTdOcXkyYmVTa0ZRSU5MN1k1c0FNVjVfdy92aWV3Zm9ybT9lbWJlZGRlZD10cnVlIiB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI3MDAiIGZyYW1lYm9yZGVyPSIwIiBtYXJnaW5oZWlnaHQ9IjAiIG1hcmdpbndpZHRoPSIwIj5Mb2FkaW5n4oCmPC9pZnJhbWU+PC9kaXY+DQoNCg0KDQojICoqTW90aXZhdGlvbioqDQoqKiogDQoNClRoaXMgY2FzZSBzdHVkeSBpcyBtb3RpdmF0ZWQgYnkgdGhpcyBbYXJ0aWNsZV0oaHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9jb250ZW50L3BkZi8xMC4xMDA3L3MxMTkyMC0wMTItMDMzMS02LnBkZik6DQoNCiMjIyMgey5yZWZlcmVuY2VfYmxvY2t9DQoNCkZsYW5uZXJ5LCBELiBKLiwgTW9kemVsZXNraSwgVy4gJiBLcmV0c2NobWFyLCBKLiBNLiBWaW9sZW5jZSBhbmQgU2Nob29sIFNob290aW5ncy4gQ3VyciBQc3ljaGlhdHJ5IFJlcCAxNSwgMzMxICgyMDEzKS4gRE9JOiBbMTAuMTAwNy9zMTE5MjAtMDEyLTAzMzEtNl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvczExOTIwLTAxMi0wMzMxLTYpDQoNCiMjIyMNCg0KVGhlIGFydGljbGUgZXhwbG9yZXMgY2hhcmFjdGVyaXN0aWNzIG9mIHNjaG9vbCBzaG9vdGluZ3MgYW5kIHZpb2xlbmNlIGluIHNjaG9vbHMgYW5kIGRpc2N1c3NlcyB3aHkgdGhlc2UgZXZlbnRzIG1heSBvY2N1ciwgYXMgd2VsbCBhcyB0aGVpciBpbXBhY3Qgb24gdGhlIGNvbW11bml0aWVzIGluIHdoaWNoIHRoZXkgb2NjdXIuDQoNClRoZSBhcnRpY2xlIGFsc28gc3RhdGVzIHRoYXQgdGhlIHNob290ZXJzIGFyZSBtb3N0IGNvbW1vbmx5IHdoaXRlIG1hbGVzLCBidXQgdGhhdCBtYW55IHByZXZpb3VzIHN0dWRpZXMgb2Ygc2hvb3RlciBjaGFyYWN0ZXJpc3RpY3MgY291bGQgbm90IGlkZW50aWZ5IGFueSBwYXJ0aWN1bGFyICJwcm9maWxlIiBvZiBzaG9vdGVycy4NCg0KPiAiVG8gZGF0ZSwgc3R1ZGllcyBvZiBzY2hvb2wgc2hvb3RpbmdzIGhhdmUgY29uY2x1ZGVkIHRoYXQgbm8NCmNvbnNpc3RlbnQgYW5kIHJlbGlhYmxlIHByb2ZpbGUgb2Ygc2Nob29sIHNob290ZXJzIGV4aXN0Li4uIg0KDQpIb3dldmVyIHByZXZpb3VzIHN0dWRpZXMgbm90ZSBzb21lIGNvbW1vbmFsaXRpZXMgc3VjaCBhczoNCg0KPiAiLi4ubW9zdCBzaG9vdGVycyB3ZXJlIGRlcHJlc3NlZCwgaGFkIGV4cGVyaWVuY2VkIHNvbWUgc2lnbmlmaWNhbnQgbG9zcywgZmVsdCBwZXJzZWN1dGVkIG9yIGJ1bGxpZWQgYnkgb3RoZXJzLCBhbmQgaGFkIHByaW9yIGRpZmZpY3VsdHkgY29waW5nIG9yIGhhZCBwcmV2aW91c2x5IHRyaWVkIHN1aWNpZGUuIiANCg0KVGhlcmVmb3JlIGluIG91ciBkYXNoYm9hcmQgd2Ugd2lsbCBleGFtaW5lIGhvdyBvZnRlbiBhIHNob290ZXIgd2FzIG1hbGUgb3IgYXR0ZW1wdGVkIG9yIGNvbW1pdHRlZCBzdWljaWRlIGR1cmluZyBhbiBldmVudC4NCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9ICI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImpvc2h1YS1ob2VobmUtQ0Fva2d4MUdHS0UtdW5zcGxhc2guanBnIikpDQpgYGANCg0KPHNwYW4+UGhvdG8gYnkgPGEgaHJlZj0iaHR0cHM6Ly91bnNwbGFzaC5jb20vQG1ydGhldHJhaW4/dXRtX3NvdXJjZT11bnNwbGFzaCZhbXA7dXRtX21lZGl1bT1yZWZlcnJhbCZhbXA7dXRtX2NvbnRlbnQ9Y3JlZGl0Q29weVRleHQiPkpvc2h1YSBIb2VobmU8L2E+IG9uIDxhIGhyZWY9Imh0dHBzOi8vdW5zcGxhc2guY29tL3MvcGhvdG9zL2hpZ2gtc2Nob29sP3V0bV9zb3VyY2U9dW5zcGxhc2gmYW1wO3V0bV9tZWRpdW09cmVmZXJyYWwmYW1wO3V0bV9jb250ZW50PWNyZWRpdENvcHlUZXh0Ij5VbnNwbGFzaDwvYT48L3NwYW4+DQoNCj4gIlNjaG9vbCBzaG9vdGluZ3MgYXJlIG5vdCBhbGwgdGhlIHNhbWUgYW5kIG1heSByZXF1aXJlIGRpZmZlcmVudCBhcHByb2FjaGVzIHRvIHByZXZlbnRpb24gYW5kIHRyZWF0bWVudCwgZXNwZWNpYWxseSB3aXRoIHJlc3BlY3QgdG8gaWRlbnRpZnlpbmcgcmlzayBmYWN0b3JzIGF0IHRoZSBpbmRpdmlkdWFsLCBzY2hvb2wNCm9yIGNvbW11bml0eSBsZXZlbHMsIGFuZCBwYXJ0aWN1bGFybHkgd2l0aCByZWdhcmQgdG8gZXhhbWluaW5nIHRoZSByb2xlIHRoYXQgbWVudGFsIGhlYWx0aCBpc3N1ZXMgbWF5IHBsYXkgdG8gaW5jcmVhc2UgcmlzayBmb3IgcGVycGV0cmF0aW9uLiANCg0KPiBUaGUgZmllbGQgKipuZWVkcyB0byBrbm93IG1vcmUqKiBhYm91dCBzaG9vdGluZyBpbmNpZGVudHMgdGhhdCBhcmUgYXZlcnRlZCwgdGhvc2UgdGhhdCByZXN1bHQgaW4gaW5qdXJ5IGJ1dCBub3QgZGVhdGggYW5kIGFib3V0IHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIG1vcmUgY29tbW9uIG9jY3VycmVuY2Ugb2Ygc2luZ2xlIGhvbWljaWRlIHNjaG9vbCBzaG9vdGluZ3MuIg0KDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0gIjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYW5kcmUtaHVudGVyLUFROTA4RmZkQU13LXVuc3BsYXNoLmpwZyIpKQ0KYGBgDQoNCjxzcGFuPlBob3RvIGJ5IDxhIGhyZWY9Imh0dHBzOi8vdW5zcGxhc2guY29tL0BkcmUwMzE2P3V0bV9zb3VyY2U9dW5zcGxhc2gmYW1wO3V0bV9tZWRpdW09cmVmZXJyYWwmYW1wO3V0bV9jb250ZW50PWNyZWRpdENvcHlUZXh0Ij5BbmRyZSBIdW50ZXI8L2E+IG9uIDxhIGhyZWY9Imh0dHBzOi8vdW5zcGxhc2guY29tL3MvcGhvdG9zL2hpZ2gtc2Nob29sP3V0bV9zb3VyY2U9dW5zcGxhc2gmYW1wO3V0bV9tZWRpdW09cmVmZXJyYWwmYW1wO3V0bV9jb250ZW50PWNyZWRpdENvcHlUZXh0Ij5VbnNwbGFzaDwvYT48L3NwYW4+DQoNCg0KR2l2ZW4gdGhpcyBuZWVkIGZvciBtb3JlIHJlc2VhcmNoIHRvIGJldHRlciB1bmRlcnN0YW5kIHdoeSB0aGVzZSBldmVudHMgb2NjdXIgYW5kIGhvdyB0aGV5IGNvdWxkIGJlIGF2ZXJ0ZWQsIGluIHRoaXMgY2FzZSBzdHVkeSB3ZSB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byBjcmVhdGUgYSByZXNvdXJjZSBmb3Igb3RoZXJzIHRvIG1vcmUgZWFzaWx5IGFuZCBpbnRlcmFjdGl2ZWx5IGFjY2VzcyBkYXRhIGFib3V0IHNjaG9vbCBzaG9vdGluZ3MuIFRvIGRvIHNvIHdlIHdpbGwgY3JlYXRlIHdoYXQgaXMgY2FsbGVkIGEgW2Rhc2hib2FyZF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGFzaGJvYXJkXyhidXNpbmVzcykpLCB3aGljaCBpcyBhIHdlYnNpdGUgdGhhdCBkaXNwbGF5cyBhIHJlcG9ydCBmb3IgYSBkYXRhYmFzZS4gRGFzaGJvYXJkcyBzdW1tYXJpemUgdGhlIGRhdGEgaW4gYSBkYXRhYmFzZSBhbmQgdHlwaWNhbGx5IGFsbG93IGZvciB1c2VycyB0byBpbnRlcmFjdCB3aXRoIHRoZSBkYXRhIGluIHNvbWUgd2F5Lg0KDQpbSGVyZV0oaHR0cHM6Ly9qamFsbGFpcmUuc2hpbnlhcHBzLmlvL3NoaW55LWNyYW5kYXNoLyNkYXNoYm9hcmRsKSB5b3UgY2FuIHNlZSBhbiBleGFtcGxlIG9mIGEgZGFzaGJvYXJkIGNyZWF0ZWQgaW4gUiBhYm91dCBkb3dubG9hZHMgb2YgcGFja2FnZXMgb24gW0NSQU5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykuDQoNCg0KT24gdGhlIHdlYnNpdGUgdGhlIHRhYnMgYW5kIHBsb3RzIGFyZSBpbnRlcmFjdGl2ZS4gVGhlIGFib3ZlIGRhc2hib2FyZCBhbGxvd3MgZm9yIHVzZXJzIHRvIGdldCB0byBrbm93IHRoZSBkYXRhIGluIGEgc2ltcGxlIGFuZCBxdWljayB3YXkuDQoNClRoZSBkYXRhIGFib3V0IHBhY2thZ2UgZG93bmxvYWRzIGlzIHN1Y2NpbmN0bHkgc3VtbWFyaXplZCBpbiBhbiBpbXBhY3RmdWwgbWFubmVyLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcubGluayA9ICJodHRwczovL2pqYWxsYWlyZS5zaGlueWFwcHMuaW8vc2hpbnktY3JhbmRhc2gvI2Rhc2hib2FyZGwifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImRhc2hib2FyZC5wbmciKSkNCmBgYA0KDQpXZSBjYW4gcXVpY2tseSBnZXQgYSBzZW5zZSB0aGF0IHRoZSBgbWFncml0dHJgIHBhY2thZ2UgaXMgYW1vbmcgdGhlIHRvcCBtb3N0IHdpZGVseSBkb3dubG9hZGVkIHBhY2thZ2VzIG9uIENSQU4uDQoNCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL2pqYWxsYWlyZS5zaGlueWFwcHMuaW8vc2hpbnktY3JhbmRhc2gvI2Rhc2hib2FyZGwpDQoNCk5vdyBsZXQncyBsZWFybiBob3cgdG8gY3JlYXRlIGEgZGFzaGJvYXJkIHdpdGggb3VyIGRhdGEgb2YgaW50ZXJlc3QuDQoNCiMgKipNYWluIFF1ZXN0aW9ucyoqDQoqKiogDQoNCiMjIyMgey5tYWluX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IE91ciBtYWluIHF1ZXN0aW9uczogPC91PjwvYj4NCg0KMSkgV2hhdCBoYXMgYmVlbiB0aGUgeWVhcmx5IHJhdGUgb2Ygc2Nob29sIHNob290aW5ncyBhbmQgd2hlcmUgaW4gdGhlIGNvdW50cnkgaGF2ZSB0aGV5IG9jY3VycmVkIGluIHRoZSBsYXN0IDUwIHllYXJzIChmcm9tIEphbnVhcnkgMTk3MCB0byBKdW5lIDIwMjApPyANCg0KMikgSG93IG1hbnkgaW5kaXZpZHVhbHMgYXJlIHR5cGljYWxseSBraWxsZWQgaW4gYSBzY2hvb2wgc2hvb3Rpbmc/DQoNCjMpIFdoYXQgd2VyZSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBzaG9vdGVyczogSG93IG9mdGVuIHdhcyBhIHNob290ZXIgbWFsZT8gSG93IG9mdGVuIGRpZCBhIHNob290ZXIgYXR0ZW1wdCBvciBjb21taXQgc3VpY2lkZT8NCg0KIyMjIw0KDQoNCiMgKipMZWFybmluZyBPYmplY3RpdmVzKiogDQoqKiogDQoNCkluIHRoaXMgY2FzZSBzdHVkeSwgd2Ugd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gY3JlYXRlIGEgW2Rhc2hib2FyZF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGFzaGJvYXJkXyhidXNpbmVzcykpLCB3aGljaCBpcyBhIHdlYnNpdGUgdGhhdCBkaXNwbGF5cyBhIHJlcG9ydCBhYm91dCBhIGRhdGFiYXNlLiBJbiBkb2luZyBzbywgd2Ugd2lsbCBmb2N1cyBvbiBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zIGZyb20gdGhlIFtgdGlkeXZlcnNlYF0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gZm9yIHRoZSBkYXRhIHdyYW5nbGluZyBhbmQgdmlzdWFsaXphdGlvbiBzZWN0aW9ucy4gVGhlIHRpZHl2ZXJzZSBpcyBhIGxpYnJhcnkgb2YgcGFja2FnZXMgY3JlYXRlZCBieSBSU3R1ZGlvLiBXaGlsZSBzb21lIHN0dWRlbnRzIG1heSBiZSBmYW1pbGlhciB3aXRoIHByZXZpb3VzIFIgcHJvZ3JhbW1pbmcgcGFja2FnZXMsIHRoZXNlIHBhY2thZ2VzIG1ha2UgZGF0YSBzY2llbmNlIGluIFIgbW9yZSBodW1hbi1yZWFkYWJsZSBhbmQgaW50dWl0aXZlLg0KDQoNCmBgYHtyLCBvdXQud2lkdGggPSAiMjAlIiwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIifQ0KaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly90aWR5dmVyc2UudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpDQpgYGANCg0KVGhlIHNraWxscywgbWV0aG9kcywgYW5kIGNvbmNlcHRzIHRoYXQgc3R1ZGVudHMgd2lsbCBiZSBmYW1pbGlhciB3aXRoIGJ5IHRoZSBlbmQgb2YgdGhpcyBjYXNlIHN0dWR5IGFyZToNCg0KDQo8dT4qKkRhdGEgU2NpZW5jZSBMZWFybmluZyBPYmplY3RpdmVzOioqPC91PiAgDQogIA0KMS4gSW1wb3J0aW5nIHRleHQgZnJvbSBhIEdvb2dsZSBTaGVldHMgZG9jdW1lbnQgKGBnb29nbGVzaGVldHM0YCkgIA0KMi4gQ29udmVydGluZyBkYXRlIGZvcm1hdHMgKGBsdWJyaWRhdGVgKSAgDQozLiBHZW9jb2RpbmcgZGF0YSAoYGdnbWFwYCkgIGFuZCBjcmVhdGluZyBhIGppdHRlciBmb3IgZ2VvY29kZWQgZGF0YSBvbiBhIG1hcCAoYFNGYCkNCjQuIEhvdyB0byByZXNoYXBlIGRhdGEgYnkgcGl2b3RpbmcgYmV0d2VlbiAibG9uZyIgYW5kICJ3aWRlIiBmb3JtYXRzIGFuZCBkcm9wIHJvd3Mgd2l0aCBgTkFgIHZhbHVlcyAoYHRpZHlyYCkgIA0KNS4gSG93IHRvIGNyZWF0ZSBkYXRhIHZpc3VhbGl6YXRpb25zIHdpdGggYGdncGxvdDJgIA0KNi4gQW4gaW50cm9kdWN0aW9uIHRvIHRoZSBiYXNpY3Mgb2YgUiBNYXJrZG93bg0KNy4gSG93IHRvIGNyZWF0ZSBhbiBpbnRlcmFjdGl2ZSB0YWJsZSAoYERUYCkgIA0KOC4gSG93IHRvIGNyZWF0ZSBhIG1hcCAoYGxlYWZsZXRgKSAgDQo5LiBIb3cgdG8gY3JlYXRlIGFuIGludGVyYWN0aXZlIGRhc2hib2FyZCB3aXRoIGBmbGV4ZGFzaGJvYXJkYCBhbmQgYHNoaW55YCAgDQoNCjx1PioqU3RhdGlzdGljYWwgTGVhcm5pbmcgT2JqZWN0aXZlczoqKjwvdT4gICAgDQoNCjEuIENhbGN1bGF0aW5nIHBlcmNlbnRhZ2VzIGZvciBkYXRhIHdpdGggbWlzc2luZyB2YWx1ZXMgIA0KMi4gQ3JlYXRpbmcgW3N1bW1hcnkgc3RhdGlzdGljc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3VtbWFyeV9zdGF0aXN0aWNzKQ0KDQoqTm90ZTogc3RhdGlzdGljcyBpcyBhIHBhcnQgb2YgZGF0YSBzY2llbmNlKg0KDQoqKiogDQoNCg0KV2Ugd2lsbCBiZWdpbiBieSBsb2FkaW5nIHRoZSBwYWNrYWdlcyB0aGF0IHdlIHdpbGwgbmVlZDoNCg0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGdvb2dsZXNoZWV0czQpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dtYXApDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZvcmNhdHMpDQpsaWJyYXJ5KGdnZm9yY2UpDQpsaWJyYXJ5KHdhZmZsZSkNCmxpYnJhcnkocG9saXNjaWRhdGEpDQpsaWJyYXJ5KGZsZXhkYXNoYm9hcmQpDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShtYXBzKQ0KbGlicmFyeSh2ZW1iZWRyKQ0KbGlicmFyeShPQ1NkYXRhKQ0KYGBgDQoNCk5vdGUgW3NvbWUgb2YgdGhlc2UgcGFja2FnZXNdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvcGFja2FnZXMvKSBhcmUgcGFydCBvZiB0aGUgYHRpZHl2ZXJzZWAgYW5kIGNhbiBiZSBsb2FkZWQgdG9nZXRoZXIgbGlrZSBzbzoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCiA8dT4qKlBhY2thZ2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5OioqIDwvdT4NCg0KUGFja2FnZSAgIHwgVXNlIGluIHRoaXMgY2FzZSBzdHVkeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCi0tLS0tLS0tLS0gfC0tLS0tLS0tLS0tLS0NCltoZXJlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpe3RhcmdldD0iX2JsYW5rIn0gICAgICAgfCB0byBlYXNpbHkgbG9hZCBhbmQgc2F2ZSBkYXRhICANCltyZWFkcl0oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnLykgfCAgdG8gaW1wb3J0IHRoZSBkYXRhICBhcyBhIGNzdiBmaWxlICANCltnb29nbGVzaGVldHM0XShodHRwczovL2dvb2dsZXNoZWV0czQudGlkeXZlcnNlLm9yZy8pIHwgdG8gaW1wb3J0IGRpcmVjdGx5IGZyb20gR29vZ2xlIFNoZWV0cw0KW3RpYmJsZV0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy8pIHwgdG8gY3JlYXRlIHRpYmJsZXMgKHRoZSB0aWR5dmVyc2UgdmVyc2lvbiBvZiBkYXRhZnJhbWVzKQ0KW2RwbHlyXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBmaWx0ZXIsIHN1YnNldCwgam9pbiwgYWRkIHJvd3MgdG8sIGFuZCBtb2RpZnkgdGhlIGRhdGEgIA0KW3N0cmluZ3JdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gbWFuaXB1bGF0ZSAgY2hhcmFjdGVyIHN0cmluZ3Mgd2l0aGluIHRoZSBkYXRhIChjb2xsYXBzaW5nIHN0cmluZ3MgdG9nZXRoZXIsIHJlcGxhY2UgdmFsdWVzLCBhbmQgZGV0ZWN0IHZhbHVlcykNClttYWdyaXR0cl0oaHR0cHM6Ly9tYWdyaXR0ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gcGlwZSBzZXF1ZW50aWFsIGNvbW1hbmRzIA0KW3RpZHlyXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBjaGFuZ2UgdGhlIHNoYXBlIG9yIGZvcm1hdCBvZiB0aWJibGVzIHRvIHdpZGUgYW5kIGxvbmcsIHRvIGRyb3Agcm93cyB3aXRoIGBOQWAgdmFsdWVzLCBhbmQgdG8gc2VlIHRoZSBsYXN0IGZldyBjb2x1bW5zIG9mIGEgdGliYmxlDQpbZ2dtYXBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ21hcC9nZ21hcC5wZGYpIHwgdG8gZ2VvY29kZSB0aGUgZGF0YSAod2hpY2ggbWVhbnMgZ2V0IHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIHZhbHVlcykNCltzZl0oaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmLykgfCB0byBtb2RpZnkgdGhlIGdlb2NvZGVkIGRhdGEgc28gdGhhdCBvdmVybGFwcGluZyBwb2ludHMgZGlkIG5vdCBvdmVybGFwDQpbbHVicmlkYXRlXShodHRwczovL2x1YnJpZGF0ZS50aWR5dmVyc2Uub3JnLykgfCB0byB3b3JrIHdpdGggdGhlIGRhdGEtdGltZSBkYXRhICAgIA0KW0RUXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL0RULykgfCB0byBjcmVhdGUgdGhlIGludGVyYWN0aXZlIHRhYmxlICANCltodG1sdG9vbHNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9odG1sdG9vbHMvdmVyc2lvbnMvMC41LjApIHwgdG8gYWRkIGEgY2FwdGlvbiB0byBvdXIgaW50ZXJhY3RpdmUgdGFibGUgDQpbZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBjcmVhdGUgcGxvdHMgIA0KW2dnZm9yY2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2ZvcmNlL2dnZm9yY2UucGRmKSAgIHwgdG8gY3JlYXRlIGEgcGxvdCB6b29tDQpbZm9yY2F0c10oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byByZW9yZGVyIGZhY3RvciBmb3IgcGxvdA0KW3dhZmZsZV0oaHR0cHM6Ly9naXRodWIuY29tL2hyYnJtc3RyL3dhZmZsZSkgfCB0byBtYWtlIHdhZmZsZSBwcm9wb3J0aW9uIHBsb3RzICANCltwb2xpc2NpZGF0YV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3BvbGlzY2lkYXRhL3BvbGlzY2lkYXRhLnBkZikgfCB0byBnZXQgcG9wdWxhdGlvbiB2YWx1ZXMgZm9yIHRoZSBzdGF0ZXMNCltmbGV4ZGFzaGJvYXJkXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkLykgICAgIHwgdG8gY3JlYXRlIHRoZSBkYXNoYm9hcmQgIA0KW3NoaW55XShodHRwczovL3NoaW55LnJzdHVkaW8uY29tLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gYWxsb3cgb3VyIGRhc2hib2FyZCB0byBiZSBpbnRlcmFjdGl2ZSAgIA0KW2xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC9zaGlueS5odG1sKSB8IHRvIGltcGxlbWVudCB0aGUgW2xlYWZsZXRdKGh0dHA6Ly9sZWFmbGV0anMuY29tLykgKGEgSmF2YVNjcmlwdCBsaWJyYXJ5IGZvciBtYXBzKSB0byBjcmVhdGUgdGhlIG1hcCBmb3Igb3VyIGRhc2hib2FyZCAgIA0KW21hcHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYXBzL21hcHMucGRmKSB8IHRvIGNyZWF0ZSB0aGUgc2ltcGxlIGxlYWZsZXQgbWFwIGV4YW1wbGUgICANClt2ZW1iZWRyXShodHRwczovL2dpdGh1Yi5jb20vaWpseXR0bGUvdmVtYmVkcikgfCB0byBpbmNsdWRlIGEgdmlkZW8gaW4gb3VyIGNhc2Ugc3R1ZHkgDQpbT0NTZGF0YV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9PQ1NkYXRhKSB8IHRvIGFjY2VzcyBhbmQgZG93bmxvYWQgT0NTIGRhdGEgZmlsZXMNCg0KDQoNClRoZSBmaXJzdCB0aW1lIHdlIHVzZSBhIGZ1bmN0aW9uLCB3ZSB3aWxsIHVzZSB0aGUgYDo6YCB0byBpbmRpY2F0ZSB3aGljaCBwYWNrYWdlIHdlIGFyZSB1c2luZy4gVW5sZXNzIHdlIGhhdmUgb3ZlcmxhcHBpbmcgZnVuY3Rpb24gbmFtZXMsIHRoaXMgaXMgbm90IG5lY2Vzc2FyeSwgYnV0IHdlIHdpbGwgaW5jbHVkZSBpdCBoZXJlIHRvIGJlIGluZm9ybWF0aXZlIGFib3V0IHdoZXJlIHRoZSBmdW5jdGlvbnMgd2Ugd2lsbCB1c2UgY29tZSBmcm9tLg0KDQoNCiMgKipDb250ZXh0KioNCioqKiANCg0KU2Nob29sIHNob290aW5ncyBnZXQgYSBsb3Qgb2YgYXR0ZW50aW9uIGluIHRoZSB0aGUgbWVkaWEsIGJ1dCBpdCB3b3VsZCBiZSBoZWxwZnVsIHRvIHNlZSBhbGwgdGhlIGRhdGEgb24gdGhlbSBhdCBvbmNlIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZW0uIEEgZGFzaGJvYXJkIGNhbiBoZWxwIHdpdGggdGhpcywgc28gdGhhdCBwZW9wbGUgZ2V0IGEgYm9hcmRlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBpc3N1ZSByYXRoZXIgdGhhbiBoZWFyaW5nIGFib3V0IHNpbmd1bGFyIHNwZWNpZmljIGluY2lkZW5jZXMgZnJvbSB0aGUgbWVkaWEuDQoNCkluIGFkZGl0aW9uIHRvIGluanVyaWVzIGFuZCBkZWF0aHMsIHNob290aW5nIGV2ZW50cyBjYW4gYWxzbyBoYXZlIGJyb2FkIGFuZCBsYXN0aW5nIGltcGFjdHMgZm9yIHRob3NlIHdobyB3aXRuZXNzIGJ1dCBhcmUgbm90IGRpcmVjdGx5IGludm9sdmVkLiANCg0KQWNjb3JkaW5nIHRvIHRoZSBbQ2VudGVyIGZvciBJbmp1cnkgUmVzZWFyY2ggYW5kIFByZXZlbnRpb24gYXQgdGhlIENoaWxkcmVuJ3MgSG9zcGl0YWwgb2YgUGhpbGFkZWxwaGlhXShodHRwczovL2luanVyeS5yZXNlYXJjaC5jaG9wLmVkdS92aW9sZW5jZS1wcmV2ZW50aW9uLWluaXRpYXRpdmUvdHlwZXMtdmlvbGVuY2UtaW52b2x2aW5nLXlvdXRoL3NjaG9vbC1zaG9vdGluZ3MpOg0KDQo+IFRoZSBtb3N0IGNvbW1vbiBzaG9vdGluZ3Mgb24gc2Nob29sIGdyb3VuZHMgcmFyZWx5IGludm9sdmUgbGFyZ2UgbnVtYmVycyBvZiB2aWN0aW1zLCBidXQgZXZlbiBhIHNob290aW5nIG9mIGp1c3Qgb25lIHN0dWRlbnQgYXQgc2Nob29sIGhhcyByYW1pZmljYXRpb25zIGZhciBiZXlvbmQgdGhvc2UgZGlyZWN0bHkgaW52b2x2ZWQuIA0KDQo+IFN0dWRlbnRzIGFuZCBzdGFmZiB0aGF0IHdpdG5lc3Mgc2Nob29sIHNob290aW5ncyBhcmUgbGlrZWx5IHRvIHN1ZmZlciBmcm9tIFt0cmF1bWF0aWMgc3RyZXNzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UcmF1bWF0aWNfc3RyZXNzKSBzeW1wdG9tcywgYmVjb21lIGFueGlvdXMgb3IgZGVwcmVzc2VkIGFuZCBoYXZlIGdlbmVyYWwgY29uY2VybnMgYWJvdXQgdGhlaXIgc2FmZXR5LiANCg0KPiBXaGlsZSBtYW55IHdpdG5lc3NlcyB3aWxsIGhhdmUgdGVtcG9yYXJ5IHN5bXB0b21zLCBvdGhlcnMgd2lsbCBiZSBzeW1wdG9tYXRpYyBmb3IgYSBtdWNoIGxvbmdlciBwZXJpb2Qgb2YgdGltZSBhbmQgZXZlbiBkZXZlbG9wIGNocm9uaWMgcHN5Y2hpYXRyaWMgZGlzb3JkZXJzLiANCg0KPiBFdmVuIHNob3J0LXRlcm0gaW1wYWlybWVudHMgY2FuIGNhdXNlIHNldmVyZSBkaXN0cmVzcyBhbmQgaGF2ZSBwcm9mb3VuZCBlZmZlY3RzIG9uIGFjYWRlbWljIGFjaGlldmVtZW50IGFuZCB0aGUgc29jaWFsIGFuZCBlbW90aW9uYWwgZ3Jvd3RoIG9mIGltcGFjdGVkIHN0dWRlbnRzLiANCiANCg0KRnVydGhlcm1vcmUsIHNjaG9vbCBzaG9vdGluZ3MgY2FuIGhhdmUgdmFzdCBhbmQgbGFzdGluZyBpbXBhY3RzIGJlY2F1c2UgbWFueSBzdHVkZW50cyBjYW4gd2l0bmVzcyBhIHNpbmdsZSBldmVudC4NCg0KQW5vdGhlciByZWNlbnRseSBwdWJsaXNoZWQgW2FydGljbGVdKGh0dHBzOi8vc2llcHIuc3RhbmZvcmQuZWR1L3NpdGVzL2RlZmF1bHQvZmlsZXMvcHVibGljYXRpb25zLzE5LTAzNi5wZGYpIGluZGljYXRlcyB0aGF0Og0KDQo+IE92ZXIgKioyNDAsMDAwKiogQW1lcmljYW4gc3R1ZGVudHMgZXhwZXJpZW5jZWQgYSBzY2hvb2wgc2hvb3RpbmcgaW4gdGhlIGxhc3QgdHdvIGRlY2FkZXMuDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImV4cG9zZWQucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3NpZXByLnN0YW5mb3JkLmVkdS9zaXRlcy9kZWZhdWx0L2ZpbGVzL3B1YmxpY2F0aW9ucy8xOS0wMzYucGRmKQ0KIA0KVGhpcyBzdHVkeSBmb2xsb3dlZCBzdHVkZW50cyB3aG8gZXhwZXJpZW5jZWQgYSBzY2hvb2wgc2hvb3RpbmcgdGhlIFVuaXRlZCBTdGF0ZXMgYmV0d2VlbiAyMDA4IGFuZCAyMDEzIGFuZCBhc3Nlc3NlZCB0aGVpciBtZW50YWwgd2VsbC1iZWluZy4gVGhleSBmb3VuZCB0aGF0Og0KDQo+IEZhdGFsIHNjaG9vbCBzaG9vdGluZ3MgaGF2ZSBsYXJnZSBhbmQgcGVyc2lzdGVudCBpbXBhY3RzIG9uIHRoZSBtZW50YWwgaGVhbHRoIG9mIGxvY2FsIHlvdXRoLiBJbiB0aGUgdHdvIHllYXJzIGZvbGxvd2luZyBhIGZhdGFsIHNjaG9vbCBzaG9vdGluZywgdGhlIG1vbnRobHkgbnVtYmVyIG9mIGFudGlkZXByZXNzYW50IHByZXNjcmlwdGlvbnMgd3JpdHRlbiB0byBpbmRpdmlkdWFscyB1bmRlciBhZ2UgMjAgaXMgMjEuMyBwZXJjZW50IGhpZ2hlciBpbiB0aGUgc2hvb3RpbmctZXhwb3NlZCByZWxhdGl2ZSB0byB0aGUgcmVmZXJlbmNlIGFyZWFzLiANCg0KIyMjIyB7LnJlZmVyZW5jZV9ibG9ja30NCg0KUm9zc2luLVNsYXRlciwgTS4sIFNjaG5lbGwsIE0uLCBTY2h3YW5kdCwgSC4sIFRyZWpvLCBTLiAmIFVuaWF0LCBMLiBMb2NhbCBFeHBvc3VyZSB0byBTY2hvb2wgU2hvb3RpbmdzIGFuZCBZb3V0aCBBbnRpZGVwcmVzc2FudCBVc2UuIHcyNjU2MyBodHRwOi8vd3d3Lm5iZXIub3JnL3BhcGVycy93MjY1NjMucGRmICgyMDE5KSBkb2k6MTAuMzM4Ni93MjY1NjMuDQoNCiMjIyMNCg0KDQpUaHVzLCBpdCBpcyB1c2VmdWwgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGVzZSBzaG9vdGluZ3MuIEhhdmluZyBiZXR0ZXIgZGF0YSBvbiB3aGF0IHRoZXkgbG9vayBsaWtlIG5hdGlvbndpZGUgY2FuIGhlbHAgd2l0aCBpZGVudGlmeWluZyBhc3NvY2lhdGlvbnMgb2Ygc2hvb3RpbmdzIHdpdGgga2V5IGNoYXJhY3RlcmlzdGljcy4gQmV0dGVyIGRlc2NyaXB0aXZlIGluZm9ybWF0aW9uIHN1Y2ggYXMgdGhpcyBtYXkgdGhlbiBsZWFkIHRvIG1vcmUga25vd2xlZGdlIGFib3V0IGZhY3RvcnMgdGhhdCBwcmVkaWN0IHNjaG9vbCBzaG9vdGluZ3MsIHdoaWNoIGNvdWxkIGhlbHAgZGV2ZWxvcCBwcmV2ZW50aXZlIGludGVydmVudGlvbnMuIFRoaXMgd2F5LCB3ZSBtaWdodCBub3Qgb25seSBwcmV2ZW50IHRoZSBkaXJlY3QgaW52b2x2ZW1lbnQgb2Ygc3R1ZGVudHMgaW4gZnV0dXJlIGV2ZW50cywgYnV0IGFsc28gdG8gcHJldmVudCBzdHVkZW50cyBhbmQgc3RhZmYgZnJvbSB3aXRuZXNzaW5nIHRoZXNlIGV2ZW50cy4NCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICI0MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImZlcm5hbmRvLWNmZXJkby02eDJpS0dpNlNQVS11bnNwbGFzaC5qcGciKSkNCmBgYA0KDQo8c3Bhbj5QaG90byBieSA8YSBocmVmPSJodHRwczovL3Vuc3BsYXNoLmNvbS9AY2ZlcmRvP3V0bV9zb3VyY2U9dW5zcGxhc2gmYW1wO3V0bV9tZWRpdW09cmVmZXJyYWwmYW1wO3V0bV9jb250ZW50PWNyZWRpdENvcHlUZXh0Ij5GZXJuYW5kbyBAY2ZlcmRvPC9hPiBvbiA8YSBocmVmPSJodHRwczovL3Vuc3BsYXNoLmNvbS9zL3Bob3Rvcy9kZXByZXNzaW9uP3V0bV9zb3VyY2U9dW5zcGxhc2gmYW1wO3V0bV9tZWRpdW09cmVmZXJyYWwmYW1wO3V0bV9jb250ZW50PWNyZWRpdENvcHlUZXh0Ij5VbnNwbGFzaDwvYT48L3NwYW4+DQoNCiANCiMgKipMaW1pdGF0aW9ucyoqDQoqKiogDQpUaGVyZSBhcmUgc29tZSBpbXBvcnRhbnQgY29uc2lkZXJhdGlvbnMgcmVnYXJkaW5nIHRoaXMgZGF0YSBhbmFseXNpcyB0byBrZWVwIGluIG1pbmQ6IA0KDQpUaGlzIGRhc2hib2FyZCBvbmx5IHVzZXMgb25lIHNvdXJjZSBvZiBkYXRhLiBUaGVyZSBtYXkgYmUgc2Nob29sIHNob290aW5nIGV2ZW50cyB0aGF0IGFyZSBub3QgbGlzdGVkIGluIHRoaXMgZGF0YSBvciBlcnJvcnMgaW4gdGhpcyBkYXRhLg0KDQpBY2NvcmRpbmcgdG8gdGhlIGRhdGFiYXNlIHdlYnNpdGUgaXRzZWxmOg0KDQo+IlRoaXMgZGF0YWJhc2Ugd2FzIGRldmVsb3BlZCBmcm9tIG9wZW4tc291cmNlIGluZm9ybWF0aW9uIGFuZCBtYXkgaW5jbHVkZSByZXBvcnRpbmcgZXJyb3JzLiINCg0KRnVydGhlcm1vcmUsIGFjY29yZGluZyB0byB0aGlzIFthcnRpY2xlXShodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2FydGljbGUvMTAuMTAwNy9zMTE5MjAtMDEyLTAzMzEtNiksIHNjaG9vbHMgaW4gMjAxMywgc2Nob29scyB3ZXJlIG5vdCByZXF1aXJlZCB0byByZXBvcnQgc2Nob29sIHNob290aW5ncyB1bmxlc3MgdGhleSByZXN1bHRlZCBpbiBhIHN1aWNpZGUgb3IgaG9taWNpZGUuIFRoZXJlZm9yZSB0aGVyZSBtYXkgYmUgbW9yZSBldmVudHMgdGhhdCByZXN1bHQgaW4gb25seSBpbmp1cnkgb3Igbm8gaW5qdXJpZXMgb3IgZGVhdGggdGhhdCBtYXkgbm90IGJlIGluY2x1ZGVkLg0KDQpUaGVyZSBhcmUgaW5kZWVkIGV2ZW50cyBpbiB0aGUgZGF0YXNldCB0aGF0IGluY2x1ZGUgemVybyBkZWF0aHMgYW5kIHplcm8gaW5qdXJpZXMsIGJ1dCBpdCBpcyB2ZXJ5IGxpa2VseSB0aGF0IG1hbnkgb2YgdGhlc2UgZXZlbnRzIGFyZSBub3QgbGlzdGVkLg0KDQojICoqV2hhdCBhcmUgdGhlIGRhdGE/KioNCioqKiANCg0KV2Ugd2lsbCB1c2UgZGF0YSBmcm9tIHRoZSBvcGVuLXNvdXJjZSBbSy0xMiBTaG9vbCBTaG9vdGluZyBEYXRhYmFzZV0oaHR0cHM6Ly93d3cuY2hkcy51cy9zc2RiL2RhdGEtbWFwLykgZnJvbSB0aGUgW0NlbnRlciBmb3IgSG9tZWxhbmQgRGVmZW5zZSBhbmQgU2VjdXJpdHldKGh0dHBzOi8vd3d3LmNoZHMudXMvYy8pIGF0IHRoZSBbTmF2YWwgUG9zdGdyYWR1YXRlIFNjaG9vbChOUFMpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9OYXZhbF9Qb3N0Z3JhZHVhdGVfU2Nob29sKSBpbiBNb250ZXJleSwgQ2FsaWZvcm5pYS4gVGhpcyBkYXRhIGlzIHVwZGF0ZWQgZGFpbHkuIFRoZSBkYXRhIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5IHdhcyBkb3dubG9hZGVkIGluIEp1bmUgb2YgMjAyMC4gDQoNCiMjIyMgey5yZWZlcmVuY2VfYmxvY2t9DQoNClJpZWRtYW4sIERhdmlkLCBhbmQgRGVzbW9uZCBP4oCZTmVpbGwuIOKAnENIRFMg4oCTIEstMTIgU2Nob29sIFNob290aW5nIERhdGFiYXNlLuKAnSBDZW50ZXIgZm9yIEhvbWVsYW5kIERlZmVuc2UgYW5kIFNlY3VyaXR5LCBKdW5lIDIwMjAsIFt3d3cuY2hkcy51cy9zc2RiXSh3d3cuY2hkcy51cy9zc2RiKS4NCg0KIyMjIw0KDQpUaGlzIGRhdGFiYXNlIGluY2x1ZGVzIGluZm9ybWF0aW9uIGFib3V0IHNjaG9vbCBzaG9vdGluZyBldmVudHMgZm9yIHN0dWRlbnRzIGluIGdyYWRlcyBLLTEyIGluIHRoZSBVbml0ZWQgU3RhdGVzIGRhdGluZyBiYWNrIHRvIDE5NzAuIFRoZSBkYXRhYmFzZSBoYXMgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBub3Qgc2hvd24gb24gb3VyIGRhc2hib2FyZCBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvOiANCg0KLSBMb2NhdGlvbiBvZiB0aGUgZXZlbnQgYXQgdGhlIHNjaG9vbCAgDQotIElmIHRoZSBldmVudCBvY2N1cnJlZCBkdXJpbmcgYSBzcG9ydGluZyBldmVudCAgDQotIFRpbWUgb2YgZGF5IG9mIHRoZSBldmVudCAgDQotIERheSBvZiB0aGUgd2VlayBvZiB0aGUgZXZlbnQgIA0KLSBTb3VyY2UgZm9yIHRoZSBzaG9vdGluZyBpbmZvcm1hdGlvbiAgDQotIElmIHRoZSBldmVudCB3YXMgcHJlLXBsYW5uZWQgb3Igbm90ICANCi0gU2hvb3RlcidzIGFjdGlvbnMgaW1tZWRpYXRlbHkgZm9sbG93aW5nIHRoZSBzaG9vdGluZyAgDQotIFNob290ZXIgY2hhcmFjdGVyaXN0aWNzIChhZmZpbGlhdGlvbiB3aXRoIHRoZSBzY2hvb2wsIGlmIHRoZXkgaGFkIGFjY29tcGxpY2VzLCBpZiB0aGV5IHRvb2sgaG9zdGFnZXMsIGFuZCB0aGVpciBhZ2UgYW5kIHJhY2UpICANCi0gVmljdGltIGNoYXJhY3RlcmlzdGljcyAoYWZmaWxpYXRpb24gd2l0aCB0aGUgc2Nob29sLCBpZiB0aGV5IHdlcmUgdGFyZ2V0ZWQsIHRoZWlyIGFnZSBhbmQgcmFjZSkgIA0KDQpBY2NvcmRpbmcgdG8gdGhlIFtLLTEyIFNob29sIFNob290aW5nIERhdGFiYXNlXShodHRwczovL3d3dy5jaGRzLnVzL3NzZGIvYWJvdXQvKSB3ZWJzaXRlOg0KDQoNCj4gVGhlIFNjaG9vbCBTaG9vdGluZyBEYXRhYmFzZSBQcm9qZWN0IGlzIGNvbmR1Y3RlZCBhcyBwYXJ0IG9mIHRoZSBbQWR2YW5jZWQgVGhpbmtpbmcgaW4gSG9tZWxhbmQgU2VjdXJpdHkgKEhTeCldKGh0dHBzOi8vd3d3LmNoZHMudXMvYy9hY2FkZW1pYy1wcm9ncmFtcy9oc3gvKSBwcm9ncmFtIGF0IHRoZSBOYXZhbCBQb3N0Z3JhZHVhdGUgU2Nob29s4oCZcyBbQ2VudGVyIGZvciBIb21lbGFuZCBEZWZlbnNlIGFuZCBTZWN1cml0eSAoQ0hEUyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NlbnRlcl9mb3JfSG9tZWxhbmRfRGVmZW5zZV9hbmRfU2VjdXJpdHkpLg0KDQo+IFRoZSBkYXRhYmFzZSBjb21waWxlcyBpbmZvcm1hdGlvbiBmcm9tIG1vcmUgdGhhbiAyNSBkaWZmZXJlbnQgc291cmNlcyBpbmNsdWRpbmcgcGVlci1yZXZpZXdlZCBzdHVkaWVzLCBnb3Zlcm5tZW50IHJlcG9ydHMsIG1haW5zdHJlYW0gbWVkaWEsIG5vbi1wcm9maXRzLCBwcml2YXRlIHdlYnNpdGVzLCBibG9ncywgYW5kIGNyb3dkLXNvdXJjZWQgbGlzdHMgdGhhdCBoYXZlIGJlZW4gYW5hbHl6ZWQsIGZpbHRlcmVkLCBkZWNvbmZsaWN0ZWQsIGFuZCBjcm9zcy1yZWZlcmVuY2VkLiBBbGwgb2YgdGhlIGluZm9ybWF0aW9uIGlzIGJhc2VkIG9uIG9wZW4tc291cmNlIGluZm9ybWF0aW9uIGFuZCAzcmQgcGFydHkgcmVwb3J0aW5nLg0KDQoNCiMgKipEYXRhIEltcG9ydCoqDQoqKiogDQoNClByZXZpb3VzbHksIHRoZSB3ZWJzaXRlIGZvciB0aGlzIGRhdGEgd2FzIGxvY2F0ZWQgYXQgImh0dHBzOi8vd3d3LmNoZHMudXMvc3NkYi9kYXRhc2V0LyIgKHdoaWNoIGlzIG5vIGxvbmdlciBhbiBhY3RpdmUgbGluayksIHdoaWNoIGRpc3BsYXllZCBhbiBhY3RpdmUgR29vZ2xlIFNoZWV0cyBkb2N1bWVudCBhbmQgYSBsaW5rIHRvIGRvd25sb2FkIGEgY3N2IGZpbGUgb2YgdGhlIGRhdGEuIEF0IHRoZSB0aW1lIHRoYXQgd2UgY3JlYXRlZCB0aGlzIGNhc2Ugc3R1ZHkgKEp1bmUgb2YgMjAyMCkgd2UgZG93bmxvYWRlZCB0aGUgZGF0YSBmcm9tIHRoaXMgd2Vic2l0ZS4NCg0KTm93IHRoZSBkYXRhIGNhbiBiZSBmb3VuZCBhdCB0aGlzIFtsaW5rXShodHRwczovL3d3dy5jaGRzLnVzL3NzZGIvYWJvdXQvKSBhbmQgYSBmaWxlIG9mIHRoZSByYXcgZGF0YSBjYW4gYmUgZG93bmxvYWRlZCBieSBjbGlja2luZyB0aGUgIkRPV05MT0FEIFJBVyBEQVRBIiBidXR0b24uIFRoaXMgZmlsZSB3YXMgcHJldmlvdXNseSBhIGAuY3N2YCBmaWxlLCBidXQgaXQgaXMgbm93IGFuIGAueGxzeGAgZmlsZS4gDQoNClRvIGFjY291bnQgZm9yIGNoYW5nZXMgd2l0aCB0aGlzIHdlYnNpdGUsIHdlIGhhdmUgbWFkZSB0aGUgcHJldmlvdXMgYC5jc3ZgIGZpbGUgYXZhaWxhYmxlIGZvciB5b3UgdG8gZG93bmxvYWQgdXNpbmcgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgbGlicmFyeShPQ1NkYXRhKQ0KcmF3X2RhdGEoIm9jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZCIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KYGBgDQoNCklmIHlvdSBoYXZlIHRyb3VibGUgdXNpbmcgdGhlIHBhY2thZ2UsIHlvdSBtYXkgYWxzbyBkb3dubG9hZCB0aGlzIGAuY3N2YCBmaWxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC9ibG9iL21hc3Rlci9kYXRhL3Jhdy9LLTEyX1NTREJfKFB1YmxpYyktSy0xMl9TU0RCXyhQdWJsaWMpX0xpbmtlZC5jc3YpLg0KDQpJbiBvdXIgY2FzZSwgd2UgZG93bmxvYWRlZCB0aGlzIGRhdGEgYW5kIHB1dCBpdCB3aXRoaW4gYSAicmF3IiBzdWJkaXJlY3Rvcnkgb2YgYSAiZGF0YSIgZGlyZWN0b3J5IGZvciBvdXIgcHJvamVjdC4gSWYgeW91IHVzZSBhbiBSU3R1ZGlvIHByb2plY3QsIHRoZW4geW91IGNhbiB1c2UgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBoZXJlYCBwYWNrYWdlIHRvIG1ha2UgdGhlIHBhdGggZm9yIGltcG9ydGluZyB0aGlzIGRhdGEgc2ltcGxlci4gVGhlIGBoZXJlYCBwYWNrYWdlIGF1dG9tYXRpY2FsbHkgc3RhcnRzIGxvb2tpbmcgZm9yIGZpbGVzIGJhc2VkIG9uIHdoZXJlIHlvdSBoYXZlIGEgYC5ScHJvamAgZmlsZSB3aGljaCBpcyBjcmVhdGVkIHdoZW4geW91IHN0YXJ0IGEgbmV3IFJTdHVkaW8gcHJvamVjdC4gV2UgY2FuIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIGxvb2sgZm9yIHRoZSAiSy0xMl9TU0RCXyhQdWJsaWMpLUstMTJfU1NEQl8oUHVibGljKV9MaW5rZWQuY3N2IiBmaWxlIHdpdGhpbiB0aGUgInJhdyIgZGlyZWN0b3J5IHdpdGhpbiB0aGUgImRhdGEiIGRpcmVjdG9yeSB3aXRoaW4gYSBkaXJlY3Rvcnkgd2hlcmUgb3VyIGAuUnByb2pgIGZpbGUgaXMgbG9jYXRlZCBieSBzZXBhcmF0aW5nIHRoZSBuYW1lcyBvZiB0aGVzZSBkaXJlY3RvcmllcyB1c2luZyBjb21tYXMgYW5kIGxpc3RpbmcgImRhdGEiIGZpcnN0LiANCg0KIyMjIyB7LmNsaWNrX3RvX2V4cGFuZF9ibG9ja30NCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgbW9yZSBhYm91dCBjcmVhdGluZyBuZXcgcHJvamVjdHMgaW4gUlN0dWRpby4gPC9zdW1tYXJ5Pg0KDQpZb3UgY2FuIGNyZWF0ZSBhIHByb2plY3QgYnkgZ29pbmcgdG8gdGhlIEZpbGUgbWVudSBvZiBSU3R1ZGlvIGxpa2Ugc286DQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk5ld19wcm9qZWN0LnBuZyIpKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyBkbyBzbyBieSBjbGlja2luZyB0aGUgcHJvamVjdCBidXR0b246DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwcm9qZWN0X2J1dHRvbi5wbmciKSkNCmBgYA0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpIHRvIGxlYXJuIG1vcmUgYWJvdXQgdXNpbmcgUlN0dWRpbyBwcm9qZWN0cy4gDQoNCjwvZGV0YWlscz4NCg0KIyMjIw0KDQpXZSBjYW4gaW1wb3J0IHRoZSByYXcgYC5jc3ZgIGZpbGUgdXNpbmcgdGhlIGByZWFkX2NzdigpYCBmdW5jdGlvbiBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuIExldCdzIHN0YXJ0IGJ5IG9ubHkgaW1wb3J0aW5nIHRoZSBmaXJzdCBmaXZlIHJvd3Mgd2l0aCB0aGUgYG5fbWF4YCBhcmd1bWVudCB3aGljaCBpcyB0aGUgbWF4IG51bWJlciBvZiByb3dzIHRvIHJlYWQgaW4gZnJvbSB0aGUgZmlsZS4gQnkgZG9pbmcgdGhpcywgd2UgY2FuIGNoZWNrIGZvciBlcnJvcnMgYmVmb3JlIHJlYWRpbmcgaW4gdGhlIGVudGlyZSBmaWxlLiBOb3RlIHRoYXQgeW91IHdvdWxkIG5lZWQgdG8gbW9kaWZ5IHRoZSBgZmlsZWAgYXJndW1lbnQgaWYgeW91IHNldCB5b3VyIGRhdGEgZmlsZXMgdXAgZGlmZmVyZW50bHkuIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgPC0gDQogIHJlYWRyOjpyZWFkX2NzdihmaWxlID0gDQogICAgICAgICAgICAgICAgICAgIGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiSy0xMl9TU0RCXyhQdWJsaWMpLUstMTJfU1NEQl8oUHVibGljKV9MaW5rZWQuY3N2IiksIA0KICAgICAgICAgICAgICAgICAgbl9tYXggPSA1KQ0Kc2hvb3RpbmdfZGF0YQ0KYGBgDQoNCldlIHNlZSB0aGUgZmlyc3Qgcm93IGlzIGEgc2VudGVuY2UgdGhhdCBzdGF0ZXM6IA0KDQo+ICJVcGRhdGVkIDYvMi8yMDIwIC0gVmlldyBncmFwaHMgYW5kIHJlc2VhcmNoIG1ldGhvZG9sb2d5IG9uIHd3dy5jaGRzLnVzL3NzZGIgSWYgeW91IGhhdmUgaW5mb3JtYXRpb24gYWJvdXQgb3RoZXIgaW5jaWRlbnRzLCBwbGVhc2UgZW1haWwgSzEyc3NkYkBjaGRzLnVzLiINCg0KV2UgZG8gbm90IG5lZWQgdGhpcyBpbmZvcm1hdGlvbiwgc28gd2UgY2FuIHNraXAgaXQgdXNpbmcgdGhlIGBza2lwYCBhcmd1bWVudCBvZiBgcmVhZF9jc3YoKWAgZnVuY3Rpb24uIFNwZWNpZmljYWxseSwgd2Ugc3BlY2lmeSB0aGF0IHdlIHdpc2ggdG8gb25seSBza2lwIDEgcm93IHdpdGggYHNraXAgPSAxYC4gV2UgY2FuIGFsc28gc3BlY2lmeSB0aGF0IHRoZSBuZXh0IHJvdyBzaG91bGQgYmUgdXNlZCBmb3IgY29sdW1uIG5hbWVzIHVzaW5nIHRoZSBgY29sX25hbWVzID0gVFJVRWAgYXJndW1lbnQuIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgPC0gDQogIHJlYWRyOjpyZWFkX2NzdihmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJyYXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkstMTJfU1NEQl8oUHVibGljKS1LLTEyX1NTREJfKFB1YmxpYylfTGlua2VkLmNzdiIpLCANCiAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IFRSVUUsIHNraXAgPSAxKQ0KYGBgDQoNCldlIGNhbiB1c2UgdGhlIGBnbGltcHNlYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgdG8gdGFrZSBhIGxvb2sgYXQgY29sdW1ucyB3aXRoaW4gdGhlIGRhdGFiYXNlOg0KDQojIyMjIHsuc2Nyb2xsYWJsZSB9DQpgYGB7cn0NCiMgU2Nyb2xsIHRocm91Z2ggdGhlIG91dHB1dCENCmRwbHlyOjpnbGltcHNlKHNob290aW5nX2RhdGEpDQpgYGANCg0KIyMjIw0KDQpXZSBjYW4gYWxzbyB1c2UgdGhlIHV0aWxzIGBzdHIoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIHNob3J0IGZvciAic3RydWN0dXJlIiB0byBzZWUgbW9yZSBkZXRhaWxzIGFib3V0IHRoZSBpbnRlcm5hbCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEuIFRoZXJlZm9yZSwgdGhlIGBzdHIoKWAgZnVuY3Rpb24gd2lsbCBnaXZlIHVzIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGFjdHVhbCB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uIHdpdGhpbiB0aGUgZGF0YSwgbm90IGp1c3QgdGhlIGNvbHVtbnMgdGhlbXNlbHZlcy4gVHlwaWNhbGx5IHdlIHdvdWxkIGJlIGFibGUgdG8gc2VlIHNvbWUgb2YgdGhlIHZhbHVlcyB3aXRoIGBnbGltcHNlKClgIGZ1bmN0aW9uIGFzIHdlbGwsIGJ1dCBzb21lIG9mIHRoZSBjb2x1bW5zIGhhdmUgdmVyeSBsb25nIG5hbWVzLCB0aHVzIG9ic2N1cmluZyB0aGUgZmlyc3QgZmV3IHZhbHVlcyBpbiB0aGUgb3V0cHV0LiANCg0KIyMjIyB7LnNjcm9sbGFibGUgfQ0KYGBge3J9DQojIFNjcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhDQpzdHIoc2hvb3RpbmdfZGF0YSkNCmBgYA0KDQoNCg0KV2UgY2FuIHNlZSBmcm9tIHRoaXMgdGhhdCBtYW55IG9mIHRoZSB2YXJpYWJsZXMgaGF2ZSBZZXMgb3IgTm8gdmFsdWVzLCB3aGlsZSBvdGhlcnMgaGF2ZSByZWxhdGl2ZWx5IGxvbmcgZGVzY3JpcHRpb25zLiBZb3UgbWF5IGFsc28gbm90aWNlIHRoYXQgdGhlIGBTdGF0ZWAgdmFsdWVzIGFyZSBzdGF0ZSBhYmJyZXZpYXRpb25zLCBub3QgZnVsbCBzdGF0ZSBuYW1lcy4gVGhpcyBpcyBzb21ldGhpbmcgdGhhdCB3ZSB3aWxsIGFkZCB0byB0aGUgZGF0YSBsYXRlci4gDQoNCg0KIyMjIw0KDQpBbHRlcm5hdGl2ZWx5LCBpZiB3ZSB3YW50ZWQgdG8gbWFrZSBhIGRhc2hib2FyZCB0aGF0IGNvbnRpbnVhbGx5IHVwZGF0ZWQgYXMgZGF0YSBnb3QgdXBkYXRlZCwgd2UgY291bGQgZG8gdGhlIGZvbGxvd2luZyB0byBpbXBvcnQgdGhlIGRhdGEgZGlyZWN0bHkgZnJvbSBhIGxpdmUgR29vZ2xlIFNoZWV0cyBkb2N1bWVudCBhcyBwcmV2aW91c2x5IHRoaXMgd2FzIGF2YWlsYWJsZSBmb3IgdGhpcyBkYXRhLg0KDQpUbyBkbyBzbyB3ZSB3b3VsZCB1c2UgdGhlIGByZWFkX3NoZWV0KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBnb29nbGVzaGVldHM0YCBwYWNrYWdlLiBUeXBpY2FsbHkgYXV0aGVudGljYXRpb24gaXMgcmVxdWlyZWQsIChtZWFuaW5nIHRoYXQgeW91IHdvdWxkIG5lZWQgdG8gc2lnbiBpbiB3aXRoIHlvdXIgR29vZ2xlIGFjY291bnQgdXNpbmcgYSB1c2VybmFtZSBhbmQgb3IgcGFzc3dvcmQpLCBidXQgc2luY2UgdGhpcyB3YXMgYSBwdWJsaWMgc2hlZXQgd2UgZGlkIG5vdCBuZWVkIHRvIHdvcnJ5IGFib3V0IGF1dGhlbnRpY2F0aW9uLiBUbyBhdm9pZCBiZWluZyBhc2tlZCBhYm91dCB0aGlzIHdlIHVzZWQgdGhlIGBnczRfZGVhdXRoKClgIGZ1bmN0aW9uIHdoaWNoIHB1dHMgdGhlIHBhY2thZ2UgaW50byBhIGRlLWF1dGhvcml6ZWQgc3RhdGUgdGhhdCB3aWxsIG5vdCBhc2sgZm9yIHVzZXJzIHRvIHNpZ24gaW4uIA0KDQoNCmBgYHtyfQ0KZ29vZ2xlc2hlZXRzNDo6Z3M0X2RlYXV0aCgpDQpgYGANCg0KR3JlYXQsIG5vdyB3ZSB3b3VsZCBuZWVkIHRvIGdldCB0aGUgc2hhcmVkIGxpbmsgZnJvbSB0aGUgZG9jdW1lbnQuIFdlIGNvdWxkIHByZXZpb3VzbHkgZG8gc28gYnkgY2xpY2tpbmcgb24gdGhlIGxpbmsgdG8gdGhlIGFjdHVhbGx5IEdvb2dsZSBTaGVldHMgZG9jdW1lbnQgbGlrZSBzbzoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0d2lkdGggPSAiNTAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnZXR0b2RvYy5wbmciKSkNCmBgYA0KDQpUaGVuIHdlIGNhbiBjbGljayBvbiB0aGUgInNoYXJlIiBidXR0b24gdG8gZ2V0IGFjY2VzcyB0byB0aGUgbGluazoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0d2lkdGggPSAiNTAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnZXR0b3NoYXJlLnBuZyIpKQ0KYGBgDQoNCkZpbmFsbHkgd2UgY2FuIGNsaWNrIG9uICJjb3B5IGxpbmsiIGJ1dHRvbiB0byBjb3B5IHRoZSBsaW5rOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICI1MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImdldGxpbmsucG5nIikpDQpgYGANCg0KT25jZSB5b3UgY29weSBhIGxpbmsgbGlrZSB0aGlzLCB5b3UgY2FuIHVzZSB0aGUgYHJlYWRfc2hlZXQoKWAgZnVuY3Rpb24gdG8gaW1wb3J0IHRoZSBkYXRhIGJ5IHNpbXBseSBwYXN0aW5nIHRoZSBsaW5rIGluIHF1b3RlcywgbGlrZSBzbzoNCg0KYGBge3IgZXZhbCA9IEZBTFNFfQ0KZGF0YV91cmwgPC0gImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFIcWJmTXhuazlYM19tUXZMeVdfTEVVZTNZeXI3Y1hNUGZ3cVVWZmRxN3NZL2VkaXQ/dXNwPXNoYXJpbmciDQoNCmdvb2dsZXNoZWV0X2RhdGEgPC0gDQogIHJlYWRfc2hlZXQoZGF0YV91cmwpDQpgYGANCg0KVGhpcyBpcyBhIGdyZWF0IG9wdGlvbiwgaG93ZXZlciwgd2UgY2hvc2Ugbm90IHRvIGRvIHRoaXMgZm9yIHRoaXMgY2FzZSBzdHVkeSB0byBhbGxvdyB0aGlzIHR1dG9yaWFsIHRvIGJlIG1vcmUgZWFzaWx5IG1haW50YWluZWQgb3ZlciB0aW1lLiBUaGlzIHdhcyBldmlkZW50bHkgYSBnb29kIGNob2ljZSBzaW5jZSB0aGUgZGF0YSBpcyBubyBsb25nZXIgYWNjZXNzaWJsZSBpbiB0aGUgc2FtZSB3YXkuIA0KDQojIyMjDQoNClRvIGFsbG93IHVzZXJzIHRvIHNraXAgaW1wb3J0IHdlIHdpbGwgc2F2ZSB0aGUgZGF0YSBhcyBhbiBSREEgZmlsZToNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCnNhdmUoc2hvb3RpbmdfZGF0YSwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAic2hvb3RpbmdfZGF0YS5yZGEiKSkNCmBgYA0KDQojICoqRGF0YSBFeHBsb3JhdGlvbiBhbmQgV3JhbmdsaW5nKioNCioqKg0KSWYgeW91IGhhdmUgYmVlbiBmb2xsb3dpbmcgYWxvbmcgYnV0IHN0b3BwZWQsIHdlIGNvdWxkIGxvYWQgb3VyIGltcG9ydGVkIGRhdGEgbGlrZSBzbzoNCmBgYHtyfQ0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgImltcG9ydGVkIiwgInNob290aW5nX2RhdGEucmRhIikpDQpgYGANCg0KIyMjIyB7LmNsaWNrX3RvX2V4cGFuZF9ibG9ja30NCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgaW1wb3J0IHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5Pg0KDQpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSBgT0NTZGF0YWAgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmBgYA0KDQpUaGVuLCB5b3UgbWF5IGRvd25sb2FkIHRoZSBpbXBvcnRlZCBkYXRhIGAucmRhYCBmaWxlIHVzaW5nIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb246DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBsaWJyYXJ5KE9DU2RhdGEpDQppbXBvcnRlZF9kYXRhKCJvY3MtYnAtc2Nob29sLXNob290aW5ncy1kYXNoYm9hcmQiLCBvdXRwYXRoID0gZ2V0d2QoKSkNCiMgbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAiaW1wb3J0ZWQiLCAic2hvb3RpbmdfZGF0YS5yZGEiKSkNCmBgYA0KDQpUbyBsb2FkIHRoZSBkb3dubG9hZGVkIGRhdGEgaW50byB5b3VyIGVudmlyb25tZW50LCB5b3UgbWF5IGRvdWJsZSBjbGljayBvbiB0aGUgYC5yZGFgIGZpbGUgaW4gUlN0dWRpbyBvciB1c2luZyB0aGUgYGxvYWQoKWAgZnVuY3Rpb24uDQoNCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkL3RyZWUvbWFzdGVyL2RhdGEvaW1wb3J0ZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkL21hc3Rlci9kYXRhL2ltcG9ydGVkL3Nob290aW5nX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gV2UgcmVjb21tZW5kIHVzaW5nIGFuIFJTdHVkaW8gcHJvamVjdCBhbmQgdGhlIFtgaGVyZWAgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBuYXZpZ2F0ZSB0byB5b3VyIGZpbGUgbW9yZSBlYXNpbHkuIA0KDQpXZSBoYXZlIHB1dCB0aGlzIGZpbGUgaW4gYSBkaXJlY3RvcnkgY2FsbGVkICJpbXBvcnRlZCIgd2l0aGluIGEgZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgd2l0aGluIG91ciB3b3JraW5nIGRpcmVjdG9yeSAod2hpY2ggaGFzIGEgLlJwcm9qIGZpbGUpLg0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJpbXBvcnRlZCIsICJzaG9vdGluZ19kYXRhLnJkYSIpKQ0KYGBgDQoNCjxociBzdHlsZT0iaGVpZ2h0OjFweDtib3JkZXI6bm9uZTtjb2xvcjojMzMzO2JhY2tncm91bmQtY29sb3I6IzMzMzsiIC8+DQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIG1vcmUgYWJvdXQgY3JlYXRpbmcgbmV3IHByb2plY3RzIGluIFJTdHVkaW8uIDwvc3VtbWFyeT4NCg0KWW91IGNhbiBjcmVhdGUgYSBwcm9qZWN0IGJ5IGdvaW5nIHRvIHRoZSBGaWxlIG1lbnUgb2YgUlN0dWRpbyBsaWtlIHNvOg0KDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJOZXdfcHJvamVjdC5wbmciKSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gZG8gc28gYnkgY2xpY2tpbmcgdGhlIHByb2plY3QgYnV0dG9uOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicHJvamVjdF9idXR0b24ucG5nIikpDQpgYGANCg0KU2VlIFtoZXJlXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzKSB0byBsZWFybiBtb3JlIGFib3V0IHVzaW5nIFJTdHVkaW8gcHJvamVjdHMuIA0KDQo8L2RldGFpbHM+DQo8aHIgc3R5bGU9ImhlaWdodDoxcHg7Ym9yZGVyOm5vbmU7Y29sb3I6IzMzMztiYWNrZ3JvdW5kLWNvbG9yOiMzMzM7IiAvPg0KPC9kZXRhaWxzPg0KDQojIyMjDQoNCkx1Y2tpbHksIG91ciBkYXRhIGlzIGFscmVhZHkgaW4gcHJldHR5IGdvb2Qgc2hhcGUsIGJ1dCB3ZSB3YW50IHRvIG1ha2Ugb3VyIGRhdGEgbW9yZSB1c2VmdWwgZm9yIG91ciBkYXNoYm9hcmQuIA0KDQoNCiMjICoqQWRkaW5nIHN0YXRlIG5hbWUqKg0KKioqDQoNCkl0IHdvdWxkIGJlIHVzZWZ1bCB0byBoYXZlIHRoZSBmdWxsIHN0YXRlIG5hbWUgaW4gb3VyIGRhdGEsIHJhdGhlciB0aGFuIGp1c3QgdGhlIGFiYnJldmlhdGlvbi4NCg0KV2UgY2FuIGRvIHNvIGJ5IHVzaW5nIGRhdGEgcmVsYXRlZCB0byB0aGUgVVMgNTAgc3RhdGVzIGluIGEgZGF0YXNldCBjYWxsZWQgYHN0YXRlYCB0aGF0IGlzIGF1dG9tYXRpY2FsbHkgbG9hZGVkIHdpdGggUiBzZXNzaW9ucyBpbiB0aGUgYGRhdGFzZXRzYCBwYWNrYWdlLiBUaGUgYHN0YXRlLmFiYmAgb2JqZWN0IGlzIGEgbGlzdCBvZiB0aGUgc3RhdGUgYWJicmV2aWF0aW9ucyBhbmQgYHN0YXRlLm5hbWVgIGlzIGEgbGlzdCBvZiB0aGUgc3RhdGUgbmFtZXMuDQoNCmBgYHtyfQ0Kc3RhdGUuYWJiDQpzdGF0ZS5uYW1lDQpgYGANCg0KV2Ugd2lsbCBjb21iaW5lIHRoZXNlIHVzaW5nIHRoZSBgdGliYmxlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGB0aWJibGUoKWAgcGFja2FnZS4gDQoNCmBgYHtyfQ0Kc3RhdGVfZGYgPC0gDQogIHRpYmJsZShTdGF0ZV9hYmIgPSBzdGF0ZS5hYmIsIA0KICAgICAgICAgU3RhdGUgPSBzdGF0ZS5uYW1lKQ0KDQpzbGljZV9oZWFkKHN0YXRlX2RmLCBuPTQpDQpgYGANCg0KTm93IHdlIHdpbGwgY29tYmluZSB0aGlzIHdpdGggb3VyIGRhdGEgdXNpbmcgdGhlIGBsZWZ0X2pvaW4oKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlLg0KVGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byBqb2luIGRhdGEgdXNpbmcgdGhlIGBkcGx5cmAgcGFja2FnZS4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0d2lkdGggPSAiNTAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJqb2luLnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9qb2luLmh0bWwpDQoNCkhlcmUgaXMgIGEgdmlzdWFsaXphdGlvbiBvZiB0aGVzZSBvcHRpb25zOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICIxMCUifQ0KIyBpbm5lcl9qb2luDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL2lubmVyLWpvaW4uZ2lmIikNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vZ2l0aHViLmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vYmxvYi9tYXN0ZXIvaW1hZ2VzL2lubmVyLWpvaW4uZ2lmKQ0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICIxMCUifQ0KIyBsZWZ0X2pvaW4NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZ2FkZW5idWllL3RpZHlleHBsYWluL21hc3Rlci9pbWFnZXMvbGVmdC1qb2luLmdpZiIpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL2dpdGh1Yi5jb20vZ2FkZW5idWllL3RpZHlleHBsYWluL2Jsb2IvbWFzdGVyL2ltYWdlcy9sZWZ0LWpvaW4uZ2lmKQ0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICIxMCUifQ0KIyByaWdodF9qb2luDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL3JpZ2h0LWpvaW4uZ2lmIikNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vZ2l0aHViLmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vYmxvYi9tYXN0ZXIvaW1hZ2VzL3JpZ2h0LWpvaW4uZ2lmKQ0KDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dHdpZHRoID0gIjEwJSJ9DQojIGZ1bGxfam9pbg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vbWFzdGVyL2ltYWdlcy9mdWxsLWpvaW4uZ2lmIikNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vZ2l0aHViLmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vYmxvYi9tYXN0ZXIvaW1hZ2VzL2Z1bGwtam9pbi5naWYpDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9qb2luLmh0bWwpIGZvciBtb3JlIGRldGFpbHMgYWJvdXQgam9pbmluZyBkYXRhLg0KDQpXZSBwcm9iYWJseSBoYXZlIGRhdGEgZm9yIGFsbCBmaWZ0eSBzdGF0ZXMsIGJ1dCB0aGVyZSBtYXkgbm90IGhhdmUgYmVlbiBzY2hvb2wgc2hvb3RpbmdzIGluIGFsbCA1MCBzdGF0ZXMgaW4gdGhpcyBkYXRhc2V0LCB0aGVyZWZvcmUgd2UgZG9uJ3Qgd2FudCB0byB1c2UgdGhlIGBmdWxsX2pvaW4oKWAgZnVuY3Rpb24uIA0KDQpXZSBhbHNvIGRvbid0IHdhbnQgdGhlIGBpbm5lcl9qb2luKClgIGZ1bmN0aW9uIGJlY2F1c2UgYERDYCBkb2VzIG5vdCBoYXZlIGEgc3RhdGUgbmFtZS4gQWNjb3JkaW5nIHRvIFdpa2lwZWRpYToNCg0KPiBUaGUgW1UuUy4gQ29uc3RpdHV0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VLlMuX0NvbnN0aXR1dGlvbikgcHJvdmlkZXMgZm9yIGEgW2ZlZGVyYWwgZGlzdHJpY3RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZlZGVyYWxfZGlzdHJpY3QpIHVuZGVyIHRoZSBbZXhjbHVzaXZlIGp1cmlzZGljdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGlzdHJpY3Rfb2ZfQ29sdW1iaWFfaG9tZV9ydWxlKSBvZiBbQ29uZ3Jlc3NdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1VuaXRlZF9TdGF0ZXNfQ29uZ3Jlc3MpOyB0aGUgZGlzdHJpY3QgaXMgdGhlcmVmb3JlIG5vdCBhIHBhcnQgb2YgYW55IFtVLlMuIHN0YXRlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VLlMuX3N0YXRlKSAobm9yIGlzIGl0IG9uZSBpdHNlbGYpDQoNClRodXMgd2Ugd2lsbCB1c2UgdGhlIGBsZWZ0X2pvaW4oeCx5KWAgZnVuY3Rpb24gd2hlcmUgYHhgIGluIHRoaXMgY2FzZSB3aWxsIGJlIHRoZSBgc2hvb3RpbmdfZGF0YWAgKGFzIGl0IGlzIGludHJvZHVjZWQgdG8gdGhpcyBjb2RlIGZpcnN0IHRocm91Z2ggdGhlIGAlPD4lYCBjb21wb3VuZCBhc3NpZ25tZW50IHBpcGUgb3BlcmF0b3IpIGFuZCBgeWAgaXMgdGhlIGBzdGF0ZV9kZmAuIFRodXMsIHdlIGFkZCB0aGUgYHN0YXRlX2RmYCB2YWx1ZXMgd2hlcmUgdGhleSBtYXRjaCB0byB0aGUgYHNob290aW5nX2RhdGFgLg0KDQoNClRoZSBgJTw+JWAgY29tcG91bmQgb3BlcmF0b3IgYWxsb3dzIHVzIHRvIHVzZSB0aGUgYW4gaW5wdXQgYW5kIHJlYXNzaWduIGl0IGF0IHRoZSBlbmQgYWZ0ZXIgYWxsIHRoZSBzdWJzZXF1ZW50IHN0ZXBzIGhhdmUgYmVlbiBwZXJmb3JtZWQuIFdlIGNhbiB0aGVyZWZvcmUgdXNlIGBkYXRhX2lucHV0ICU8PiVgIGluc3RlYWQgb2YgYGRhdGFfaW5wdXQgPC0gZGF0YV9pbnB1dCAlPiVgLiBXZSB3aWxsIGRlbW9uc3RyYXRlIHRoaXMgaW4gdGhlIGNvZGUgYmVsb3cuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPD4lDQogIHJlbmFtZSgiU3RhdGVfYWJiIiA9IFN0YXRlKSAlPiUNCiAgbGVmdF9qb2luKHN0YXRlX2RmLCBieSA9IGMoIlN0YXRlX2FiYiIgPSAiU3RhdGVfYWJiIikpDQpgYGANCg0KDQpJbiBjb250cmFzdCwgd2UgY2FuIGp1c3QgdXNlIHRoZSBgJT4lYCBwaXBlIG9wZXJhdG9yIHRvIHNlbGVjdCBhIHNldCBvZiBjb2x1bW5zIGFuZCBwZWVrIGF0IHRoZSBmaXJzdCBmb3VyIHJvd3Mgb2YgdGhlIG5ldyBkYXRhIGZyYW1lLiAgDQoNCiMjIyMgey5jbGlja190b19leHBhbmRfYmxvY2t9DQoNCjxkZXRhaWxzPiA8c3VtbWFyeT5DbGljayBoZXJlIGlmIHlvdSBhcmUgdW5mYW1pbGlhciB3aXRoIHBpcGluZyBpbiBSLCB3aGljaCB1c2VzIHRoaXMgYCU+JWAgb3BlcmF0b3IuPC9zdW1tYXJ5PiAgDQoNCkJ5IFtwaXBpbmddKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYWdyaXR0ci92aWduZXR0ZXMvbWFncml0dHIuaHRtbCkgd2UgbWVhbiB1c2luZyB0aGUgYCU+JWAgcGlwZSBvcGVyYXRvciB3aGljaCBpcyBhY2Nlc3NpYmxlIGFmdGVyIGxvYWRpbmcgdGhlIGB0aWR5dmVyc2VgIG9yIHNldmVyYWwgb2YgdGhlIHBhY2thZ2VzIHdpdGhpbiB0aGUgdGlkeXZlcnNlIGxpa2UgYGRwbHlyYCBiZWNhdXNlIHRoZXkgbG9hZCB0aGUgW2BtYWdyaXR0cmAgcGFja2FnZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ3JpdHRyL3ZpZ25ldHRlcy9tYWdyaXR0ci5odG1sKS4gDQpUaGlzIGFsbG93cyB1cyB0byBwZXJmb3JtIG11bHRpcGxlIHNlcXVlbnRpYWwgc3RlcHMgb24gb25lIGRhdGEgaW5wdXQuDQpUaGUgb2JqZWN0IG9uIHRoZSBsZWZ0IHNpZGUgaXMgdXNlZCBhcyBpbnB1dCB0byBhbnkgY29tbWFuZHMgdG8gdGhlIHJpZ2h0IG9yIGJlbG93Lg0KDQo8L2RldGFpbHM+ICANCg0KIyMjIw0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lDQogIHNlbGVjdChTY2hvb2wsIENpdHksIFN0YXRlX2FiYiwgU3RhdGUpICU+JQ0KICBzbGljZV9oZWFkKG4gPSA0KQ0KYGBgDQoNCiMjICoqUmVmb3JtYXR0aW5nIGRhdGVzKioNCioqKg0KDQpXZSBhbHNvIHdhbnQgdG8gcmVmb3JtYXQgb3VyIGRhdGUgdmFsdWVzIGFuZCBjcmVhdGUgYSBgRGF0ZV95ZWFyYCB2YXJpYWJsZSBiYXNlZCBvbiB0aGUgeWVhciBpbiBlYWNoIGRhdGUuIFdlIGNhbiB1c2UgdGhlIGBsdWJyaWRhdGVgIHBhY2thZ2UgZm9yIHRoaXMuDQoNClRoZSBgbWR5KClgIGZ1bmN0aW9uIGNvbnZlcnRzIGRhdGVzIGludG8gYSBmb3JtYXQgd2hlcmUgZGF0ZXMgYXJlIGxpc3RlZCBhcyBtb250aCwgZGF0ZSwgYW5kIHllYXIgd2l0aCBoeXBoZW5zIGluIGJldHdlZW4uDQpUaGUgYHllYXIoKWAgZnVuY3Rpb24gY2FuIHRoZW4gYmUgdXNlZCB0byBleHRyYWN0IGp1c3QgdGhlIHllYXIgZnJvbSBlYWNoIGRhdGUuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPD4lDQogIG11dGF0ZShEYXRlID0gbHVicmlkYXRlOjptZHkoRGF0ZSkpICU+JQ0KICBtdXRhdGUoRGF0ZV95ZWFyID0gbHVicmlkYXRlOjp5ZWFyKERhdGUpKQ0KDQpzaG9vdGluZ19kYXRhICU+JSANCiAgc2VsZWN0KERhdGUsIERhdGVfeWVhcikNCmBgYA0KTG9va3MgZ29vZCENCg0KIyMgKipSZWZvcm1hdHRpbmcgZGF0YSB0eXBlcyoqDQoqKioNCg0KSWYgeW91IHJlY2FsbCwgaW4gb3VyIGRhdGFzZXQgd2UgaGF2ZSBtYW55IHZhcmlhYmxlcyB0aGF0IGhhdmUgZWl0aGVyIGBZZXNgIG9yIGBOb2AgdmFsdWVzIG9yIGBZYCBhbmQgYE5gIHZhbHVlcy4gIA0KDQpgYGB7cn0NCm5hbWVzKHNob290aW5nX2RhdGEpDQpgYGANCg0KKk5vdGUgdGhhdCBpbiB0aGlzIGNhc2Ugc3R1ZHksIHdlIHdpbGwgbW9zdGx5IGJlIHZpc3VhbGl6aW5nIHRoZSBkYXRhLiBIb3dldmVyLCBmb3IgbW9yZSBpbnRlbnNpdmUgYW5hbHlzaXMsIGl0IHdvdWxkIGJlIGJldHRlciB0byBtYWtlIG5hbWVzIG1vcmUgdGlkeSwgc3VjaCBhcyB1c2luZyBsb3dlcmNhc2UgYW5kIG5vIHNwYWNlcyBldGMuKg0KDQpUaGVzZSBhcmUgdGhlIHZhcmlhYmxlcyB0aGF0IGhhdmUgYFkvTmAgaW4gdGhlIG5hbWUgb3IgdGhlIGBUYXJnZXRlZCBTcGVjaWZpYyBWaWN0aW0ocylgLCBgUmFuZG9tIFZpY3RpbXNgLCBgUHJlLXBsYW5uZWQgc2Nob29sIGF0dGFja2AgdmFyaWFibGVzLiANCg0KV2UgY2FuIG1ha2UgdGhlc2UgY29uc2lzdGVudGx5IGBUUlVFYCBhbmQgYEZBTFNFYCBieSB1c2luZyB0aGUgYGNhc2Vfd2hlbigpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UuIFRoaXMgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIHNwZWNpZnkgbmV3IHZhbHVlcyBmb3IgZXhpc3RpbmcgdmFsdWVzLg0KDQojIyMjIHsuY2xpY2tfdG9fZXhwYW5kX2Jsb2NrfQ0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IElmIHlvdSBhcmUgZmFtaWxpYXIgd2l0aCB0aGUgYHJlY29kZSgpYCBmdW5jdGlvbiBmcm9tIGBkcGx5cmAsIGNsaWNrIGhlcmUgZm9yIGFuIGV4cGxhbmF0aW9uIG9mIHdoeSBgY2FzZV93aGVuKClgIGlzIGJldHRlciBpbiB0aGlzIGNhc2UuIDwvc3VtbWFyeT4NCg0KVGhlIGJlbmVmaXQgb2YgdGhlIGBjYXNlX3doZW4oKWAgZnVuY3Rpb24sIGlzIHRoYXQgY2hhbmdpbmcgdGhlIHZhbHVlcyB0byBgVFJVRWAgb3IgYEZBTFNFYCBhbHNvIHJlc3VsdHMgaW4gdGhlIGNsYXNzIHR5cGUgb2YgdGhlIHZhcmlhYmxlIGNoYW5naW5nIHRvIHR5cGUgbG9naWNhbCAod2hpY2ggaXMgaW50ZXJwcmV0ZWQgYXMgYSBiaW5hcnkgdmFyaWFibGUgd2l0aCBgVFJVRWAgYW5kIGBGQUxTRWAgdmFsdWVzKSBvdGhlcndpc2UsIHdpdGggYHJlY29kZSgpYCB0aGUgdmFyaWFibGVzIHdvdWxkIHJlbWFpbiBhcyBjbGFzcyB0eXBlIGNoYXJhY3Rlci4gDQoNCjxociBzdHlsZT0iaGVpZ2h0OjFweDtib3JkZXI6bm9uZTtjb2xvcjojMzMzO2JhY2tncm91bmQtY29sb3I6IzMzMzsiIC8+DQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgZm9yIGFuIGV4cGxhbmF0aW9uIGFib3V0IGRhdGEgdHlwZXMgaW4gUi4gPC9zdW1tYXJ5Pg0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBjbGFzc2VzIG9mIGRhdGEgaW4gUiBwcm9ncmFtbWluZy4gDQpDaGFyYWN0ZXIgaXMgb25lIG9mIHRoZXNlIGNsYXNzZXMuIA0KQSBjaGFyYWN0ZXIgc3RyaW5nIGlzIGFuIGluZGl2aWR1YWwgZGF0YSB2YWx1ZSBtYWRlIHVwIG9mIGNoYXJhY3RlcnMuIA0KVGhpcyBjYW4gYmUgYSBwYXJhZ3JhcGgsIGxpa2UgdGhlIGxlZ2VuZCBmb3IgdGhlIHRhYmxlLCBvciBpdCBjYW4gYmUgYSBzaW5nbGUgbGV0dGVyIG9yIG51bWJlciBsaWtlIHRoZSBsZXR0ZXIgYCJhImAgb3IgdGhlIG51bWJlciBgIjMiYC4gDQpJZiBkYXRhIGFyZSBvZiBjbGFzcyBjaGFyYWN0ZXIsIHRoYW4gdGhlIG51bWVyaWMgdmFsdWVzIHdpbGwgbm90IGJlIHByb2Nlc3NlZCBsaWtlIGEgbnVtZXJpYyB2YWx1ZSBpbiBhIG1hdGhlbWF0aWNhbCBzZW5zZS4gDQoNCklmIHlvdSB3YW50IHlvdXIgbnVtZXJpYyB2YWx1ZXMgdG8gYmUgaW50ZXJwcmV0ZWQgdGhhdCB3YXksIHRoZXkgbmVlZCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBudW1lcmljIGNsYXNzLiANClRoZSBvcHRpb25zIHR5cGljYWxseSB1c2VkIGFyZSBpbnRlZ2VyICh3aGljaCBoYXMgbm8gZGVjaW1hbCBwbGFjZSkgYW5kIGRvdWJsZSBwcmVjaXNpb24gKHdoaWNoIGhhcyBhIGRlY2ltYWwgcGxhY2UpLiANCg0KU2ltaWxhcmx5IGlmIHlvdXIgZGF0YSBpcyBvZiBjbGFzcyBjaGFyYWN0ZXIgYW5kIGFyZSB2YWx1ZXMgb2YgYFRSVUVgIGFuZCBgRkFMU0VgIHRoZXkgd2lsbCBiZSBpbnRlcnByZXRlZCBhcyB0d28gZGlmZmVyZW50IHN0cmluZ3MuIA0KDQpIb3dldmVyLCAqKmxvZ2ljYWwgZGF0YSoqIGlzIGludGVycHJldGVkIHNsaWdodGx5IGRpZmZlcmVudGx5IHdoZXJlIGEgYEZBTFNFYCB2YWx1ZSBpbmRpY2F0ZXMgdGhlIGFic2VuY2Ugb2Ygc29tZXRoaW5nLCB3aGlsZSBhIGBUUlVFYCBpbmRpY2F0ZXMgdGhlIHByZXNlbmNlIG9mIHNvbWV0aGluZy4NCg0KPC9kZXRhaWxzPg0KDQo8aHIgc3R5bGU9ImhlaWdodDoxcHg7Ym9yZGVyOm5vbmU7Y29sb3I6IzMzMztiYWNrZ3JvdW5kLWNvbG9yOiMzMzM7IiAvPg0KDQo8ZGV0YWlscz48c3VtbWFyeT4gQ2xpY2sgaGVyZSBmb3IgbW9yZSBkZXRhaWxzIGFib3V0IHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBgcmVjb2RlKClgIGFuZCBgY2FzZV93aGVuKClgIGZ1bmN0aW9ucy4gPC9zdW1tYXJ5Pg0KDQpOb3RlIHRoYXQgd2l0aCBgcmVjb2RlKClgIHRoZXJlIGlzIHRoZSBvcHRpb24gdGhhdCBvdGhlciB2YWx1ZXMgYmUgcmVjb2RlZCB0byBgTkFgIGFsdGhvdWdoIHRoaXMgaXMgbm90IHRoZSBkZWZhdWx0LCBob3dldmVyIHdpdGggYGNhc2Vfd2hlbigpYCBvdGhlciB2YWx1ZXMgbm90IGV4cGxpY2l0bHkgYXNzaWduZWQgaW4gdGhlIGBjYXNlX3doZW4oKWAgc3RhdGVtZW50IHdpbGwgYmUgYXNzaWduZWQgdG8gYE5BYC4gRnVydGhlciBtb3JlIG9ubHkgdmFsdWVzIGNhbiBiZSB1c2VkIG9uIHRoZSBsZWZ0IHNpZGUgd2hlbiB1c2luZyBgcmVjb2RlKClgIHdoZXJlYXMgYGNhc2Vfd2hlbigpYCBhY2NlcHRzIGV4cHJlc3Npb25zLg0KDQo8L2RldGFpbHM+DQoNCjwvZGV0YWlscz4NCg0KIyMjIw0KDQpPSyBsZXQncyBzdGFydCBieSBsb29raW5nIGF0IHRoZSBjb2x1bW5zIG9mIGludGVyZXN0IGJ5IHVzaW5nIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIGFuZCBhc2tpbmcgZm9yIGFueSBwYXR0ZXJucyB0aGF0IG1hdGNoIHRoZSBjaGFyYWN0ZXIgc3RyaW5nICJZL04iIG9yICJTcGVjaWZpYyIgb3IgIlJhbmRvbSIgb3IgIlByZS1wbGFubmVkIi4gDQoNCkZvcm1hbGx5LCB3ZSBjYW4gc2VhcmNoIGZvciB0aGVzZSB1c2luZyB0aGUgYHxgIHN5bWJvbCwgd2hpY2ggaXMgaW50ZXJwcmV0ZWQgYXMgYW4gb3IsIHRodXMgYW55IHZhcmlhYmxlcyB0aGF0IGhhcyBhIG5hbWUgdGhhdCBtYXRjaGVzIGFueSBvZiB0aGVzZSBwYXR0ZXJucyB3aWxsIGJlIGNoYW5nZWQuIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lIA0KICBzZWxlY3QobWF0Y2hlcygiWS9OfFNwZWNpZmljfFJhbmRvbXxQcmUtcGxhbm5lZCIpKQ0KYGBgDQoNCldlIHNlZSB0aGUgYFllc2AgYW5kIGBOb2AgdmFsdWVzLiBMZXQncyBsb29rIGNsb3NlciBhdCBvbmUgb2YgdGhlc2UgY29sdW1ucy4gDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPiUgDQogIGNvdW50KGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApDQpgYGANCldlIHNlZSB0aGVyZSBhcmUgc2l4IGRpZmZlcmVudCB2YWx1ZXMgaW4gdGhpcyBjb2x1bW4uIFRvIHJlY29kZSB0aGlzIGNvbHVtbiwgd2UgbmVlZCB0byBjb25zaWRlciB3aGF0IHdlIHJlY29kZSBhbGwgdGhlIHZhbHVlcy4gDQoNClRvIGltcGxlbWVudCB0aGUgYGNhc2Vfd2hlbigpYCByZWNvZGluZyBvZiB2YWx1ZXMsIHRoZSBleGlzdGluZyB2YWx1ZXMgYXJlIHdyaXR0ZW4gb24gdGhlIGxlZnQgb2YgdGhlIGB+YCBzaWduIChxdW90YXRpb24gbWFya3MgYXJlIG5lY2Vzc2FyeSBhcm91bmQgdGhlIGV4aXN0aW5nIHZhbHVlcykgYW5kIG5ldyB2YWx1ZXMgYXJlIHdyaXR0ZW4gb24gdGhlIHJpZ2h0IChxdW90YXRpb25zIG1hcmtzIGFyZSBub3QgbmVjZXNzYXJ5IGFzIHRoZXNlIGFyZSBgVFJVRWAgYW5kIGBGQUxTRWAgc3RhdGVtZW50cykuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPiUNCiAgICAgICBzZWxlY3QoYFN1aWNpZGUgKG9yIGF0dGVtcHRlZCBzdWljaWRlKSBieSBTaG9vdGVyIChZL04pYCkgJT4lDQogICAgICAgbXV0YXRlKHR5cGUgPSBkcGx5cjo6Y2FzZV93aGVuKC4gPT0gIlllcyIgfiBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID09ICJObyIgfiBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLiA9PSAiWSIgfiBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID09ICJOIiB+IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID09ICJPZmZpY2VyIEludm9sdmVkIiB+IFRSVUUpKQ0KYGBgDQoNCkluIHRoZSBhYm92ZSBjb2RlIGNodW5rLCB3ZSBkaWQgdGhpcyBmb3Igb25lIG9mIHRoZSBjb2x1bW5zLCBidXQgbm93IGxldCdzIGRvIGZvciBhbGwgdGhlIGNvbHVtbnMgdGhhdCBtYXRjaGVkIG91ciBzdHJpbmcgIlkvTnxTcGVjaWZpY3xSYW5kb218UHJlLXBsYW5uZWQiIGFzIGFib3ZlLiANCg0KVG8gZG8gdGhpcywgd2Ugd2lsbCB1c2UgdGhlIGBhY3Jvc3MoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlIGFuZCB0aGUgYG1hdGNoZXMoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHRpZHlzZWxlY3RgIHBhY2thZ2UgdG8gYWxsb3cgdXMgdG8gYXBwbHkgdGhpcyB0byBhbGwgb2YgdGhlIHZhcmlhYmxlcyB0aGF0IGhhdmUgYSBwYXR0ZXJuIHRoYXQgdGhhdCBtYXRjaGVzIGFueSBvZiB0aG9zZSBvZiB0aGUgdmFyaWFibGVzIHdlIHdhbnQgdG8gY2hhbmdlLiANCg0KVGhlIGBhY3Jvc3MoKWAgZnVuY3Rpb24gdGhlbiBhcHBsaWVzIHRoZSBgY2FzZV93aGVuKClgIGZ1bmN0aW9uIHRvIGFsbCBvZiB0aGVzZSB2YXJpYWJsZXMuIE5vdGljZSB0aGF0IHRoZSBgfmAgc3ltYm9sIGlzIG5lY2Vzc2FyeSBiZWZvcmUgdGhlIGZ1bmN0aW9uIHRoYXQgaXMgYXBwbGllZCB1c2luZyBgYWNyb3NzKClgLg0KDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPD4lDQogIG11dGF0ZShkcGx5cjo6YWNyb3NzKC5jb2xzID0gbWF0Y2hlcygiWS9OfFNwZWNpZmljfFJhbmRvbXxQcmUtcGxhbm5lZCIpLA0KICAgICAgICAgICAgICAgICAgICAgICB+IGRwbHlyOjpjYXNlX3doZW4oLiA9PSAiWWVzIiB+IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID09ICJObyIgfiBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4gPT0gIlkiIH4gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4gPT0gIk4iIH4gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID09ICJPZmZpY2VyIEludm9sdmVkIiB+IFRSVUUpKSkNCmBgYA0KDQpGaW5hbGx5LCB3ZSBjYW4gY2hlY2sgb3V0IHdoYXQgaGFwcGVuZWQgYWZ0ZXIgcmVjb2RpbmcgdGhlIHZhcmlhYmxlcy4gIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lIA0KICBzZWxlY3QobWF0Y2hlcygiWS9OfFNwZWNpZmljfFJhbmRvbXxQcmUtcGxhbm5lZCIpKQ0KYGBgDQoNCkxvb2tzIGdvb2QhDQoNCiMjICoqR2VvY29kaW5nIHdpdGggdGhlIGBnZ21hcGAgcGFja2FnZSoqDQoNCkZvciB0aGUgcHVycG9zZSBvZiBvdXIgZGFzaGJvYXJkLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjcmVhdGluZyBhIG1hcC4gDQoNClRvIGRvIHRoaXMsIHdlIG5lZWQgdG8gcGVyZm9ybSBhIHByb2Nlc3MgY2FsbGVkIFtnZW9jb2RpbmddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb2NvZGluZykuIEdlb2NvZGluZyBpcyB0aGUgcHJvY2VzcyBvZiBjb252ZXJ0aW5nIGFkZHJlc3NlcyBpbnRvIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgY29vcmRpbmF0ZXMuDQoNClRvIHBlcmZvcm0gdGhlIGdlb2NvZGluZyB3ZSBuZWVkIHRoZSBhZGRyZXNzIG9mIGVhY2ggc2Nob29sIGluIHRoZSBkYXRhIHNldC4gVGhlIGRhdGEgY3VycmVudGx5IGRvZXMgbm90IGxpc3QgdGhlIGFjdHVhbCBhZGRyZXNzLCBidXQgZG9lcyBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzY2hvb2wgd2hlcmUgdGhlIGV2ZW50IG9jY3VycmVkLiANCg0KU2luY2Ugc29tZSBzY2hvb2xzIGhhdmUgdGhlIHNhbWUgbmFtZSwgd2UgbmVlZCB0aGUgY2l0eSBhbmQgc3RhdGUgZGF0YSBhcyB3ZWxsLiBTbyB3ZSB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBpbiBvdXIgZGF0YSBjYWxsZWQgYGFkZHJlc3NgIHVzaW5nIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZS4gDQoNClRoaXMgdmFyaWFibGUgd2lsbCBjb2xsYXBzZSB0aGUgdmFsdWVzIGluIHRoZSBgU2Nob29sYCwgYENpdHlgLCBhbmQgYFN0YXRlYCBjb2x1bW5zIGJ1dCB3aXRoIHNwYWNlcyBpbiBiZXR3ZWVuLiBJdCBpcyBzcGVjaWZpZWQgc3VjaCB0aGF0IHRoZXJlIHdpbGwgYmUgc3BhY2UgaW4gYmV0d2VlbiBieSB0aGUgYHNlcCA9ICIgImAgYXJndW1lbnQuIA0KDQoqKk5vdGUqKjogYSBzcGFjZSBpcyB0eXBlZCBiZXR3ZWVuIHRoZSBxdW90YXRpb24gbWFya3MuIA0KDQpJbiB0aGlzIHdheSwgd2UgdGhlbiBjYW4gdXNlIHRoZSBhZGRyZXNzIHZhcmlhYmxlIHRvIGxvb2sgdXAgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgZm9yIGVhY2ggc2Nob29sLg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJTw+JQ0KICBkcGx5cjo6bXV0YXRlKGFkZHJlc3MgPSANCiAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl9jKFNjaG9vbCwgQ2l0eSwgU3RhdGVfYWJiLCBzZXAgPSAiICIpKQ0KYGBgDQoNCldlIGNhbiB0YWtlIGEgbG9vayBhdCBqdXN0IHRoaXMgbmV3IGBhZGRyZXNzYCB2YXJpYWJsZSB1c2luZyB0aGUgYHB1bGwoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyKClgIHBhY2thZ2UuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YSAlPiUNCiAgZHBseXI6OnB1bGwoYWRkcmVzcykgJT4lIA0KICBoZWFkKCkNCmBgYA0KDQpOb3cgd2UgY2FuIHVzZSB0aGVzZSBhZGRyZXNzZXMgdG8gZmluZCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBjb29yZGluYXRlcyBmb3IgZWFjaCBzY2hvb2wgd2hlcmUgYSBzY2hvb2wgc2hvb3Rpbmcgb2NjdXJyZWQuIA0KDQpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGdlb2NvZGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGdnbWFwYCBwYWNrYWdlIHRvIGxvb2sgdXAgdGhlc2UgYWRkcmVzc2VzIG9uIEdvb2dsZSBNYXBzIHRvIGdldCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMuIEluIHRoZSBgZ2VvY29kZSgpYCBmdW5jdGlvbiwgd2UgYWxzbyBuZWVkIHRvIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIHVzZSBnb29nbGUgYXMgdGhlIHNvdXJjZSB1c2luZyB0aGUgYHNvdXJjZWAgYXJndW1lbnQgYW5kIHRoYXQgd2Ugd2FudCBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIHVzaW5nIHRoZSBgb3V0cHV0ID0gYygibGF0bG9uIilgIGFyZ3VtZW50Lg0KDQpUaGlzIHN0ZXAgcmVxdWlyZXMgcmVnaXN0ZXJpbmcgd2l0aCB0aGUgR29vZ2xlIENsb3VkIFBsYXRmb3JtIHRvIGdldCBhbiBBUEkga2V5LCB3aGljaCBjdXJyZW50bHkgcmVxdWlyZXMgcmVnaXN0ZXJpbmcgeW91ciBwYXltZW50IGluZm9ybWF0aW9uIGFuZCBhZ3JlZWluZyB0byB0aGUgW0dvb2dsZSBNYXBzIEFQSSBUZXJtcyBvZiBTZXJ2aWNlXShodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9tYXBzL3Rlcm1zKS4NCg0KWW91IGFyZSAqKm5vdCByZXF1aXJlZCB0byBkbyB0aGlzIHlvdXJzZWxmKiohIFdlIHdpbGwgZ2l2ZSB5b3UgdGhlIGRhdGEuIA0KDQojIyMjIHsuY2xpY2tfdG9fZXhwYW5kX2Jsb2NrfQ0KDQo8ZGV0YWlscz48c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgaG93IHRoaXMgcHJvY2VzcyB3b3JrcyBpbiBnZW5lcmFsLjwvc3VtbWFyeT4NCg0KPGhyIHN0eWxlPSJoZWlnaHQ6MXB4O2JvcmRlcjpub25lO2NvbG9yOiMzMzM7YmFja2dyb3VuZC1jb2xvcjojMzMzOyIgLz4NCg0KPGRldGFpbHM+PHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIGhvdyB3ZSByZWdpc3RlcmVkIHdpdGggdGhlIEdvb2dsZSBDbG91ZCBQbGF0Zm9ybS48L3N1bW1hcnk+DQoNCklmIHlvdSB3ZXJlIHRvIGRvIHRoaXMgcHJvY2VzcyB5b3Vyc2VsZiwgeW91IGNvdWxkIGdldCBhbiBBUEkga2V5IFtoZXJlXShodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vbWFwcy1wbGF0Zm9ybS8pLiBBZ2FpbiB0aGlzIHJlcXVpcmVzIHJlZ2lzdGVyaW5nIHlvdXIgcGF5bWVudCBpbmZvcm1hdGlvbiwgYnV0IGl0IGlzIGZyZWUgdG8gZ290IGFuIEFQSSBrZXkgYW5kIGVuYWJsZSB0aGUgQVBJcywgaG93ZXZlciB5b3UgY2FuIGJlIGJpbGxlZCBiYXNlZCBvbiBob3cgbWFueSBhZGRyZXNzZXMgeW91IGxvb2sgdXAgdXNpbmcgdGhlIEFQSXMuIFlvdSBuZWVkIHRvIGxvb2sgdXAgdGhvdXNhbmRzIGJlZm9yZSBnZXR0aW5nIGJpbGxlZC4NCg0KVGhlbiB5b3UgbmVlZCB0byBlbmFibGUgdGhlIG1hcHMgYW5kIHBsYWNlcyBBUElzLCBieSBjbGlja2luZyBvbiB0aGUgYm94ZXMgbmV4dCB0byBlYWNoOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImVuYWJsZS5wbmciKSkNCmBgYA0KDQpUaGVuIHlvdSB3b3VsZCByZWdpc3RlciBsaWtlIHNvIGFmdGVyIGNvcHlpbmcgdGhlIEFQSSBrZXk6IChOb3RlIHRoaXMgaXMgYSBmYWtlIGtleSkNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCmdnbWFwOjpyZWdpc3Rlcl9nb29nbGUoa2V5ID0gIm1Ra3pUcGlhTFlqUHFYUUJvdGVzZ2lmM0VmR0wyZGJyTlZPcm9nZyIpIA0KYGBgDQoNCjwvZGV0YWlscz4NCg0KPGhyIHN0eWxlPSJoZWlnaHQ6MXB4O2JvcmRlcjpub25lO2NvbG9yOiMzMzM7YmFja2dyb3VuZC1jb2xvcjojMzMzOyIgLz4NCg0KT25jZSB3ZSBoYXZlIG9idGFpbmVkIGFuIEFQSSBrZXkgYW5kIGFyZSByZWdpc3RlcmVkLCB3ZSBjYW4gZ2VvY29kZSBvdXIgZGF0YS4NCg0KTm90ZSB0aGF0IHRoaXMgc3RlcCBpcyB0aW1lIGludGVuc2l2ZSwgYXMgdGhlcmUgYXJlIG1hbnkgYWRkcmVzc2VzIHRvIGxvb2sgdXAhIFRoZXJlZm9yZSwgd2Ugd2lsbCBqdXN0IHNob3cgaG93IHRoaXMgaXMgZG9uZSBhbmQgKip3aWxsIG5vdCBldmFsdWF0ZSB0aGUgY29kZSBmb3IgdGhlIG5leHQgZmV3IGNvZGUgY2h1bmtzKiouIEFnYWluIHdlIHdpbGwgdXNlIHRoZSBgZ2VvY29kZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2dtYXBgIHBhY2thZ2UgdG8gcGVyZm9ybSB0aGlzIHN0ZXAuDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpzaG9vdGluZ19kYXRhIDwtIA0KICBzaG9vdGluZ19kYXRhICU+JQ0KICBtdXRhdGUoY29vcmRzID0gZ2dtYXA6Omdlb2NvZGUoYWRkcmVzcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQgPSBjKCJsYXRsb24iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2UgPSBjKCJnb29nbGUiKSkpDQpgYGANCg0KVGhpcyByZXN1bHRzIGluIHRpYmJsZSBjYWxsZWQgYGNvb3Jkc2AgYmVpbmcgYWRkZWQgdG8gb3VyIGBzaG9vdGluZ19kYXRhYCB0aWJibGUuIFRoYXQncyByaWdodCwgd2UgY2FuIGhhdmUgYSB0aWJibGUgYXMgYSBjb2x1bW4gb3IgdmFyaWFibGUgd2l0aGluIGEgdGliYmxlLiAgIFVzaW5nIHRoZSBgZ2xpbXBzZWAgZnVuY3Rpb24gYWdhaW4sIGFuZCBsb29raW5nIGF0IHRoZSBsYXN0IGZldyB2YXJpYWJsZXMsIHdlIGNhbiBzZWUgdGhhdCBub3cgdGhlIGxhc3QgdmFyaWFibGUgbGlzdGVkIGlzIGBjb29yZHNgIG9mIGNsYXNzIGA8dGliYmxlPmAuDQoNCmBgYHtyLGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI5MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInNob290aW5nX2RhdGFfY29vcmRzLnBuZyIpKQ0KYGBgDQoNCklmIHdlIHRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBjb3VwbGUgb2YgdmFsdWVzIG9mIHRoZSBgY29vcmRzYCB0aWJibGUsIHdlIHNlZSBhIHRpYmJsZSB0aGF0IGxvb2tzIGxpa2UgdGhpczoNCg0KDQpgYGB7cixlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iMzAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJjb29yZHMucG5nIikpDQpgYGANCg0KSXQgd291bGQgYmUgYmV0dGVyIGlmIGVhY2ggb2YgdGhlc2Ugd2VyZSB0aGVpciBvd24gY29sdW1ucyBpbiB0aGUgdGliYmxlLCBzbyB3ZSB3aWxsIGNyZWF0ZSBuZXcgYGxvbmdpdHVkZWAgYW5kIGBsYXRpdHVkZWAgdmFyaWFibGVzIGFnYWluIHVzaW5nIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiBsaWtlIHNvOg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0Kc2hvb3RpbmdfZGF0YSA8LSANCiAgc2hvb3RpbmdfZGF0YSAlPiUNCiAgbXV0YXRlKGxvbmdpdHVkZSA9IHB1bGwoY29vcmRzLGxvbiksDQogICAgICAgICBsYXRpdHVkZSA9IHB1bGwoY29vcmRzLGxhdCkpDQpgYGANCg0KSW4gdGhpcyBjYXNlIHdlIHVzZSB0aGUgYHB1bGwoKWAgZnVuY3Rpb24gdG8gZ3JhYiB0aGUgYGxhdGAgYW5kIGBsb25gIHZhcmlhYmxlcyB3aXRoaW4gdGhlIGBjb29yZHNgIHRpYmJsZSB3aGljaCBpcyBhIHZhcmlhYmxlIG9mIHRoZSBgc2hvb3RpbmdfZGF0YWAgdGliYmxlLiBUaGlzIGNhbiBhbHNvIGJlIGRvbmUgdXNpbmcgdGhlIFtgdW5wYWNrKClgIGZ1bmN0aW9uXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3BhY2suaHRtbCkgZnJvbSB0aGUgYHRpZHlyYCBwYWNrYWdlLg0KDQpXZSBjYW4gbm93IHJlbW92ZSB0aGUgYGNvb3Jkc2AgdGliYmxlIGxpa2Ugc28sIHVzaW5nIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZToNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0gIA0Kc2hvb3RpbmdfZGF0YSA8LSANCiAgc2hvb3RpbmdfZGF0YSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtY29vcmRzKQ0KYGBgDQoNCk5vdyB1c2luZyBgZ2xpbXBzZSgpYCBhbmQgbG9va2luZyBhdCB0aGUgbGFzdCBzZXZlcmFsIHZhcmlhYmxlcywgd2UgY2FuIHNlZSB0aGF0IHdlIG5vIGxvbmdlciBoYXZlIGEgYGNvb3Jkc2AgdmFyaWFibGUsIGJ1dCB3ZSBkbyBoYXZlIHR3byB2YXJpYWJsZXMgY2FsbGVkIGBsb25naXR1ZGVgIGFuZCBgbGF0aXR1ZGVgIHRoYXQgYXJlIG9mIGNsYXNzIGRvdWJsZSBhcyBpbmRpY2F0ZWQgYnkgdGhlIGA8ZGJsPmA6DQoNCmBgYHtyLGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI4MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImRvdWJsZS5wbmciKSkNCmBgYA0KDQpOb3cgd2Ugd2lsbCBzYXZlIHRoZSBnZW9jb2RlZCBkYXRhIGluIHRoZSBgd3JhbmdsZWRgIGRpcmVjdG9yeSBvZiBvdXIgYGRhdGFgIGRpcmVjdG9yeSB1c2luZyB0aGUgYHNhdmVgIGZ1bmN0aW9uLg0KDQpUaGlzIHJlcXVpcmVzIGxpc3RpbmcgdGhlIFIgb2JqZWN0LCBmb2xsb3dlZCBieSB0aGUgcGF0aCBmb3Igd2hlcmUgdGhlIGZpbGUgc2hvdWxkIGJlIHNhdmVkIGFuZCB3aGF0IGl0IHNob3VsZCBiZSBjYWxsZWQuIEluIHRoaXMgY2FzZSBpdCB3aWxsIGJlIGNhbGxlZCBgc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwLnJkYWAuIEZpcnN0IGxldCdzIGNyZWF0ZSBhIG5ldyBvYmplY3QgY2FsbGVkIGBzaG9vdGluZ19kYXRhX3dyYW5nbGVkX3ByZV9tYXBgIHNvIGl0IGlzIGNsZWFyIGluIHRoZSBmdXR1cmUgd2hhdCB3ZSBhcmUgd29ya2luZyB3aXRoIHdoZW4gd2UgbG9hZCB0aGUgZGF0YS4gV2Ugd2lsbCBhbHNvIHdyaXRlIHRoaXMgZGF0YSB0byBhIGNzdiBmaWxlLCB3aGljaCBjYW4gYmUgY29udmVuaWVudCBmb3IgY29sbGFib3JhdG9ycy4gVG8gZG8gdGhpcyB3ZSB3aWxsIHVzZSB0aGUgYHdyaXRlX2NzdigpYCBmdW5jdGlvbiBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuDQoNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCA8LSBzaG9vdGluZ19kYXRhDQpzYXZlKHNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCwgZmlsZSA9IGhlcmUoImRhdGEiLCAid3JhbmdsZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwLnJkYSIpKQ0KDQpyZWFkcjo6d3JpdGVfY3N2KHNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCwgDQogICAgICAgICAgICAgICAgIGZpbGUgPSBoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcC5jc3YiKSkNCmBgYA0KDQojIyMjDQoNCllvdSBjYW4gZG93bmxvYWQgdGhlIHdyYW5nbGVkIGRhdGEgd2l0aCBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIHZhbHVlcyB1c2luZyB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBsaWJyYXJ5KE9DU2RhdGEpDQp3cmFuZ2xlZF9yZGEoIm9jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZCIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KIyBsb2FkKGZpbGUgPSBoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwLnJkYSIpKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyBhY2Nlc3MgdGhpcyBkYXRhIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC9ibG9iL21hc3Rlci9kYXRhL3dyYW5nbGVkKS4NCg0KVG8gbG9hZCB0aGUgZGF0YSwgeW91IG1heSBkb3VibGUgY2xpY2sgdGhlIGRvd25sb2FkZWQgYC5yZGFgIGZpbGUgaW4gUlN0dWRpbywgb3IgcHV0IHRoZSBkb3dubG9hZGVkIGZpbGUgaW4gdGhlIGFwcHJvcHJpYXRlIGRpcmVjdG9yeSBhbmQgdXNlIHRoZSBmb2xsb3dpbmcgY29tbWFuZC4NCg0KYGBge3J9DQpsb2FkKGZpbGUgPSBoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcC5yZGEiKSkNCmBgYA0KDQoNCg0KIyMgKipHZW9tZXRyeSBsaXN0cyB3aXRoIHRoZSBgc2ZgIHBhY2thZ2UqKg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgdXNlIHRoZSBgc2ZgICh3aGljaCBzdGFuZHMgZm9yIHNpbXBsZSBmZWF0dXJlcykgcGFja2FnZSB0byANCmNyZWF0ZSB3aGF0IGlzIGNhbGxlZCBhIGdlb21ldHJ5IGxpc3Qgb2Ygb3VyIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgaW5mb3JtYXRpb24gZm9yIHRoZSBzY2hvb2xzIHdoZXJlIHNob290aW5ncyBvY2N1cnJlZC4gQXMgc29tZSBzY2hvb2wgc2hvb3RpbmdzIG9jY3VycmVkIGluIHRoZSBzYW1lIGxvY2F0aW9uLCB3ZSBuZWVkIHRvIGFsdGVyIGFsbCBvZiB0aGUgbG9jYXRpb25zIGEgYml0IHNvIHRoYXQgd2hlbiB3ZSBwbG90IHRoZSBkYXRhIG9uIGEgbWFwLCB0aGUgc3BvdHMgaW5kaWNhdGluZyB3aGVyZSBzY2hvb2wgc2hvb3RpbmdzIG9jY3VycmVkIHdpbGwgbm90IG92ZXJsYXAgZm9yIHRoZSBzYW1lIGxvY2F0aW9uLg0KDQpMZXQncyBsZWFybiBob3cgdG8gZG8gdGhpcy4gDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwICU+JSANCiAgc2VsZWN0KGxhdGl0dWRlLCBsb25naXR1ZGUpICU+JSANCiAgc2xpY2VfaGVhZChuID0gNSkNCmBgYA0KDQpGaXJzdCwgbGV0J3MgcmVtaW5kIG91cnNlbHZlcyBob3cgbWFueSByb3dzIHdlIGhhdmUgaW4gb3VyIGRhdGFzZXQuDQoNCmBgYHtyfQ0KZGltKHNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCkNCmBgYA0KDQpTaW5jZSB0aWJibGVzIGdpdmUgZGltZW5zaW9ucywgaW5zdGVhZCBvZiB1c2luZyB0aGUgZnVuY3Rpb24gYGRpbSgpYCwgd2UgbWlnaHQgYWxzbyBjaGVjayB0aGUgZGltZW5zaW9ucyBvZiBvdXIgZGF0YXNldCBieSBzaW1wbHkgZG9pbmcgc286DQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwDQpgYGANCg0KVGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBub3QgYWxsIHJvd3MgaGF2ZSBhIHJlY29yZGVkIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCAlPiUgDQogIGZpbHRlcihpcy5uYShsYXRpdHVkZSkpICU+JSANCiAgc2VsZWN0KGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGFkZHJlc3MpDQpgYGANCg0KVGhlcmVmb3JlLCBiZWZvcmUgd2UgY2FuIHByb2NlZWQsIHdlIG5lZWQgdG8gcmVtb3ZlIHJvd3Mgd2l0aCBgTkFgIHZhbHVlcyBmb3IgdGhlIGBsYXRpdHVkZWAgYW5kIGBsb25naXR1ZGVgIHZhcmlhYmxlcy4gSW4gb3RoZXIgd29yZHMsIHdlIG5lZWQgdG8gcmVtb3ZlIHJvd3Mgb2YgZXZlbnRzIHRoYXQgaGFwcGVuZWQgYXQgc2Nob29scyB3aXRoIGxvY2F0aW9ucyB0aGF0IHdlcmUgbm90IGlkZW50aWZpZWQgYnkgR29vZ2xlLiANCg0KV2UgY2FuIHJlbW92ZSB0aGVzZSByb3dzIHVzaW5nIHRoZSBgZHJvcF9uYSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgdGlkeXJgIHBhY2thZ2UuIFdlIHdpbGwgdXNlIGEgYC5gIHRvIGluZGljYXRlIHRoYXQgd2Ugd2FudCB0byB1c2UgdGhlIGRhdGEgdGhhdCB3ZSBhcmUgdXNpbmcgYXMgYW4gaW5wdXQgd2l0aCBvdXIgcGlwZSwgYnV0IHRoZW4gd2Ugd2lsbCBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0byBvbmx5IGRyb3Agcm93cyB3ZXJlIHRoZXJlIGlzIGFuIGBOQWAgdmFsdWUgZm9yIGVpdGhlciB0aGUgYGxhdGl0dWRlYCBvciBgbG9uZ2l0dWRlYCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwIDwtIHNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCAlPiUNCiB0aWR5cjo6ZHJvcF9uYShjKGxhdGl0dWRlLCBsb25naXR1ZGUpKQ0KYGBgDQoNCkhvdyBtYW55IGRpZCB3ZSByZW1vdmU/IExldCdzIGxvb2sgYXQgdGhlIGRpbWVuc2lvbiBvZiBvdXIgbmV3IGRhdGFzZXQuIA0KDQpgYGB7cn0NCmRpbShzaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXApDQpgYGANCg0KV2Ugc2VlIHRoYXQgdGhlcmUgd2VyZSA1IGV2ZW50cyB0aGF0IG9jY3VycmVkIGF0IHNjaG9vbHMgd2l0aCB1bmlkZW50aWZpZWQgY29tcGxldGUgbG9jYXRpb25zIChtaXNzaW5nIGVpdGhlciBsYXRpdHVkZSwgbG9uZ2l0dWRlLCBvciBib3RoKSB0aGF0IHdlcmUgcmVtb3ZlZCBmcm9tIG91ciBkYXRhc2V0LiANCg0KTmV4dCwgd2UgYXJlIHJlYWR5IHRvIGNvbnZlcnQgb3VyIGNvb3JkaW5hdGVzIHZhcmlhYmxlcyAoYGxhdGl0dWRlYCBhbmQgYGxvbmdpdHVkZWApIGludG8gYSBjb29yZGluYXRlIHNpbXBsZSBmZWF0dXJlIChvciBgc2ZgIG9iamVjdCkgdXNpbmcgdGhlIGBzdF9hc19zZigpYCBmdW5jdGlvbiAoY29udmVydHMgZm9yZWlnbiBvYmplY3QgdG8gYW4gYHNmYCBvYmplY3QpLiANCg0KVG8gZG8gdGhpcywgd2UgbmVlZCB0byBzcGVjaWZ5IHdoYXQgb3VyIGNvb3JkaW5hdGUgdmFyaWFibGVzIGFyZSBhbmQgd2Ugd2lsbCBhbHNvIHNwZWNpZnkgd2hhdCBbY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtXShodHRwczovL3d3dy53My5vcmcvMjAxNS9zcGF0aWFsL3dpa2kvQ29vcmRpbmF0ZV9SZWZlcmVuY2VfU3lzdGVtcyksKGNycykgd2Ugd291bGQgbGlrZSB0byB1c2UuIEluIG91ciBjYXNlIHdlIHdpbGwgdXNlIHRoZSBbRVNQR10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRVBTR19HZW9kZXRpY19QYXJhbWV0ZXJfRGF0YXNldCkgcmVmZXJlbmNlIG51bWJlciBbNDMyNl0oaHR0cHM6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy9yZWYvZXBzZy80MzI2LyksIGtub3duIGFzIEVTUEc6NDMyNiBvciB0aGUgW1dvcmxkIEdlb2RldGljIFN5c3RlbSAoV0dTKSB2ZXJzaW9uIDg0XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Xb3JsZF9HZW9kZXRpY19TeXN0ZW0jV0dTODQpIHdoaWNoIGlzIG9uZSBvZiB0aGUgbW9zdCBjb21tb25seSB1c2VkIENQUyBhbmQgdXNlZCBieSBieSBtb3N0IGdsb2JhbCBwb3NpdGlvbmluZyBzeXN0ZW1zLCBrbm93biBhcyBHUFMuICoqVGhpcyB0ZWxscyBSICB0byB1c2UgdGhlIHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlcyBjYWxsZWQgYGxhdGl0dWRlYCBhbmQgYGxvbmdpdHVkZWAgYXMgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBjb29yZGluYXRlcy4qKg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcCAlPD4lDQogIHNmOjpzdF9hc19zZihjb29yZHMgPSBjKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgY3JzID0gNDMyNikNCg0KZGltKHNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcCkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgb3VyIGBsYXRpdHVkZWAgYW5kIGBsb25naXR1ZGVgIHZhcmlhYmxlcyB3ZXJlIHVzZWQgdG8gY3JlYXRlIGEgc2luZ2xlIG5ldyB2YXJpYWJsZSBjYWxsZWQgYGdlb21ldHJ5YCBvZiBjbGFzcyAgYDxQT0lOVGAgWyRee1xjaXJjfSRdYD5gLCB0aHVzIHdlIGhhdmUgb25lIGxlc3MgY29sdW1uLg0KDQpJbiB0aGlzIGNhc2UsIHRoaXMgdHlwZSBvZiB2YXJpYWJsZSB3aWxsIGFsd2F5cyBiZSBzaG93bi4gRXZlbiBpZiB3ZSB3ZXJlIHRvIGxvb2sgYXQganVzdCB0aGUgIGZpcnN0IDQgdmFyaWFibGVzIHVzaW5nIGluZGV4ZXMgKGxpa2UgdGhpczogYFsxOjRdYCksIHdlIHdpbGwgYWxzbyBzZWUgb3VyIGxhc3QgYHNmYCB2YXJpYWJsZSBhcHBlbmRlZCBhdCB0aGUgZW5kLg0KDQpTbyBub3cgd2UgY2FuIHNlZSBhbGwgdmFyaWFibGVzIHJlbGF0ZWQgdG8gbG9jYXRpb24gKHdoaWNoIGhhcHBlbiB0byBiZSB0aGUgZmlyc3QgZm91ciB2YXJpYWJsZXMgYW5kIHRoZSBgZ2VvbWV0cnlgIHZhcmlhYmxlKSBieSBzaW1wbHkgdHlwaW5nIGBbMTo0XWAgbmV4dCB0byB0aGUgbmFtZSBvZiBvdXIgdGliYmxlIGBzaG9vdGluZ19kYXRhX2dlb2NvZGVkYC4NCg0KYGBge3J9DQpzaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXBbMTo0XQ0KYGBgDQoNClRvIGFsbG93IG91ciBwb2ludHMgdG8gbm90IG92ZXJsYXAgZm9yIGV2ZW50cyB0aGF0IHRvb2sgcGxhY2UgaW4gdGhlIHNhbWUgbG9jYXRpb24sIHdlIHdpbGwgYWRkIGEgYml0IG1vcmUgcmFuZ2Ugc28gdGhhdCB0aGV5IGRvIG5vdCBvdmVybGFwIG9uZSBhbm90aGVyIG9uIG91ciBtYXAuIA0KDQpUbyBkbyB0aGlzLCB3ZSB3aWxsIHRyYW5zZm9ybSB0aGUgY29vcmRpbmF0ZXMgdXNpbmcgdGhlIGBzdF90cmFuc2Zvcm0oKWAgZnVuY3Rpb24gIG9mIHRoZSBgc2ZgIHBhY2thZ2UgaW50byBhIHR3byBkaW1lbnNpb25hbCBwcm9qZWN0aW9uIChjYWxsZWQgdGhlIFtBbGJlcnMgZXF1YWwtYXJlYSBjb25pYyBwcm9qZWN0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BbGJlcnNfcHJvamVjdGlvbiM6fjp0ZXh0PVRoZSUyMEFsYmVycyUyMGVxdWFsJTJEYXJlYSUyMGNvbmljLHRoYXQlMjB1c2VzJTIwdHdvJTIwc3RhbmRhcmQlMjBwYXJhbGxlbHMuJnRleHQ9VGhlJTIwQWxiZXJzJTIwcHJvamVjdGlvbiUyMGlzJTIwdXNlZCx0aGUlMjBVbml0ZWQlMjBTdGF0ZXMlMjBDZW5zdXMlMjBCdXJlYXUuKSkgd2l0aCB1bml0cyBpbiBtZXRlcnMgdXNpbmcgdGhlIFtjcnMgMTAyMDA4XShodHRwczovL3NwYXRpYWxyZWZlcmVuY2Uub3JnL3JlZi9lc3JpLzEwMjAwOC9odG1sLykgcmVmZXJlbmNlIGZyb20gdGhlIFtFbnZpcm9ubWVudGFsIFN5c3RlbXMgUmVzZWFyY2ggSW5zdGl0dXRlIChFUlNJKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRXNyaSkgYW5kIHRoZW4gdXNlIHRoZSBgc3Rfaml0dGVyKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBzZmAgcGFja2FnZSAgdG8gYWxsb3cgYSBzcGVjaWZpZWQgYW1vdW50IG9mIHJhbmdlIG5lYXIgdGhlIGFjdHVhbCBvcmlnaW5hbCBHUFMgY29vcmRpbmF0ZXMuIA0KDQpUbyBsZWFybiBtb3JlIGFib3V0IGdlb3NwYXRpYWwgY29vcmRpbmF0ZSBzeXN0ZW1zIHNlZSBbaGVyZV0oaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvc2l0ZXMvZGVmYXVsdC9maWxlcy8yMDIwLTA0L092ZXJ2aWV3Q29vcmRpbmF0ZVJlZmVyZW5jZVN5c3RlbXMucGRmKSBhbmQgW2hlcmVdKGh0dHBzOi8vZ3VpZGVzLmxpYnJhcnkuZHVrZS5lZHUvci1nZW9zcGF0aWFsL0NSUykuDQoNClNvIGhlcmUgd2UgY2FuIHNlZSB0aGUgb3V0cHV0IGFmdGVyIHRyYW5zZm9ybWluZyBvdXIgZGF0YSB0byB0aGUgW2NycyAxMDIwMDhdKGh0dHBzOi8vc3BhdGlhbHJlZmVyZW5jZS5vcmcvcmVmL2VzcmkvMTAyMDA4L2h0bWwvKSByZWZlcmVuY2U6DQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwICAlPD4lDQogIHN0X3RyYW5zZm9ybShjcnMgPSAiRVNSSToxMDIwMDgiKSANCmBgYA0KDQoNCk5vdGljZSBob3cgdGhlICBjbGFzcyBmb3IgdGhlIGBnZW9tZXRyeWAgdmFyaWFibGUgaXMgbm93IGA8UE9JTlQgW21dPmAgYXMgb3VyIGRhdGEgaGFzIGJlZW4gdHJhbnNmb3JtZWQgaW50byBjb29yZGluYXRlcyBpbiBtZXRlcnMuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9wcmVfbWFwWzE6NV0NCmBgYA0KDQpBbmQgbm93IHdlIHdpbGwgYWRkIGEgaml0dGVyIHRvIHRoZSBwb2ludHMgdXNpbmcgdGhlIGBzdF9qaXR0ZXIoKWAgZnVuY3Rpb24sIG1lYW5pbmcgdGhhdCB3ZSB3aWxsIHJhbmRvbWx5IG1vdmUgdGhlIGNvb3JkaW5hdGVzIGEgbGl0dGxlIGJpdCB0byBhbGxvdyBmb3IgcG9pbnRzIGF0IHRoZSBzYW1lIGxvY2F0aW9uIHRvIG5vdCBvdmVybGFwIG9uIHRoZSBtYXAuDQoNCllvdSBjYW4gc2VlIHRoZSB0aWR5dmVyc2UgZXhwbGFuYXRpb24gYWJvdXQgd2hlbiB0byB1c2UgYSBqaXR0ZXIgcGxvdCAgW2hlcmVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX2ppdHRlci5odG1sKSwgdGhleSBzdGF0ZSB0aGF0IGEgaml0dGVyOg0KDQo+IGFkZHMgYSBzbWFsbCBhbW91bnQgb2YgcmFuZG9tIHZhcmlhdGlvbiB0byB0aGUgbG9jYXRpb24gb2YgZWFjaCBwb2ludCwgYW5kIGlzIGEgdXNlZnVsIHdheSBvZiBoYW5kbGluZyBvdmVycGxvdHRpbmcgY2F1c2VkIGJ5IGRpc2NyZXRlbmVzcyBpbiBzbWFsbGVyIGRhdGFzZXRzLg0KDQpJbiB0aGlzIGNhc2Ugd2Ugd2lsbCBhbGxvdyBmb3IgNTAgbWV0ZXJzIG9mIHJhbmdlIHVzaW5nIHRoZSBgYW1vdW50ID0gNTBgIGFyZ3VtZW50Lg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcCAlPD4lDQogICBzZjo6c3Rfaml0dGVyKGFtb3VudCA9IDUwKQ0KYGBgDQoNCg0KV2UgY2FuIG5vdyBzZWUgdGhhdCB0aGUgY29vcmRpbmF0ZXMgYXJlIHNsaWdodGx5IG1vZGlmaWVkLiANCg0KYGBge3J9DQpzaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXBbMTo0XQ0KYGBgDQoNCioqTm90ZSoqOiB0aGUgYGdlb21ldHJ5YCB2YWx1ZXMgaGF2ZSBjaGFuZ2VkLg0KDQpOb3cgd2Ugd2lsbCB0cmFuc2Zvcm0gb3VyIGNvb3JkaW5hdGVzIGJhY2sgaW50byB0aGUgM0QgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBkZWdyZWUgc3lzdGVtIGFnYWluIHVzaW5nIHRoZSBgc3RfdHJhbnNmb3JtKClgIGZ1bmN0aW9uIGFuZCB0aGUgW0VTUEc6NDMyNl0oaHR0cHM6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy9yZWYvZXBzZy80MzI2LyksIGNvb3JkaW5hdGUgc3lzdGVtLg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcCAgJTw+JQ0KICBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNikNCg0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwWzE6NF0NCmBgYA0KDQpOb3RpY2UgaG93IHRoZSBgZ2VvbWV0cnlgIHZhcmlhYmxlcyBhcmUgZGlmZmVyZW50IGZyb20gd2hhdCB0aGV5IHdlcmUgb3JpZ2luYWxseSB3aXRoIHRoaXMgY29vcmRpbmF0ZSBzeXN0ZW06DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0gIjkwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZ2VvbWV0cnkucG5nIikpDQpgYGANCg0KTmV4dCwgd2Ugc2VwYXJhdGUgdGhlIGBnZW9tZXRyeWAgdmFyaWFibGUgaW50byBgbG9uZ2l0dWRlYCBhbmQgYGxhdGl0dWRlYCB2YXJpYWJsZXMgYWdhaW4uIFdlIGNhbiB1c2UgdGhlIGAgc3RfY29vcmRpbmF0ZXMoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHNmYCBwYWNrYWdlIHRvIGV4dHJhY3QgdGhlIGNvb3JkaW5hdGVzIGZyb20gb3VyIHRpYmJsZSBhcyBhIG1hdHJpeC4NCg0KYGBge3J9DQpzaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXAgJTw+JSANCiAgbXV0YXRlKGNvb3JkaW5hdGVzID0gYXNfdGliYmxlKHN0X2Nvb3JkaW5hdGVzKC4pKSkNCg0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwICU+JQ0KICBwdWxsKGNvb3JkaW5hdGVzKSAlPiUNCiAgc2xpY2VfaGVhZChuID0gNCkNCmBgYA0KDQpOb3csIGp1c3QgYXMgd2UgZGlkIHByZXZpb3VzbHkgd2Ugd2lsbCBjcmVhdGUgbmV3IHZhcmlhYmxlcyBjYWxsZWQgYGxhdGl0dWRlYCBhbmQgYGxvbmdpdHVkZWAgZnJvbSB0aGUgYFhgIGFuZCBgWWAgdmFyaWFibGVzIHdpdGhpbiB0aGUgYGNvb3JkaW5hdGVzYCB0aWJibGUgdGhhdCBpcyBwYXJ0IG9mIG91ciBgc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwYCB1c2luZyB0aGUgYHB1bGwoKWAgZnVuY3Rpb24uDQoNCldlIHdpbGwgYWxzbyBjb252ZXJ0IG91ciBgc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwYCBvYmplY3Qgd2hpY2ggaXMgY3VycmVudGx5IGEgYHNmYCBpbnRvIGEgdGliYmxlIHVzaW5nIHRoZSBgYXNfdGliYmxlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGB0aWJibGVgIHBhY2thZ2UgYW5kIHRoZW4gd2Ugd2lsbCByZW1vdmUgdGhlIGBnZW9tZXRyeWAgYW5kIGBjb29yZGluYXRlc2AgdmFyaWFibGVzIHVzaW5nIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSB3aXRoIGEgbWludXMgb3BlcmF0b3IgaW4gZnJvbnQgb2YgdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgdG8gcmVtb3ZlLg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcCAlPD4lDQogIG11dGF0ZShsb25naXR1ZGUgPSBwdWxsKGNvb3JkaW5hdGVzLFgpLA0KICAgICAgICAgIGxhdGl0dWRlID0gcHVsbChjb29yZGluYXRlcyxZKSkgJT4lDQogIHRpYmJsZTo6YXNfdGliYmxlKCkgJT4lDQogIHNlbGVjdCgtZ2VvbWV0cnkpICU+JQ0KICBzZWxlY3QoLWNvb3JkaW5hdGVzKQ0KYGBgDQoNCkFuZCBub3cgd2UgY2FuIHRha2UgYSBsb29rIGF0ICBvdXIgbGFzdCAzIHZhcmlhYmxlcyB1c2luZyB0aGUgYGxhc3RfY29sKClgIGZ1bmN0aW9uLCB3aGljaCBpcyBhIGBzZWxlY3QoKWAgaGVscGVyIGZ1bmN0aW9uIGB0aWR5cmAgcGFja2FnZSAoU2VlIFtoZXJlXShodHRwczovL3RpZHlzZWxlY3Quci1saWIub3JnL3JlZmVyZW5jZS9zZWxlY3RfaGVscGVycy5odG1sKSBmb3Igb3RoZXIgYHNlbGVjdCgpYCBoZWxwZXIgZnVuY3Rpb25zKS4gDQoNClRoZSBgbGFzdF9jb2woKWAgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIHNlbGVjdCBlaXRoZXIgdGhlIGxhc3QgY29sdW1uLCBvciB3aXRoIGEgc3BlY2lmaWVkIG9mZnNldCB3ZSBjYW4gc2VsZWN0IGEgbnVtYmVyIG9mIGNvbHVtbnMgYmVmb3JlIHRoZSBsYXN0IGNvbHVtbi4gVGh1cywgMiBjb2x1bW5zIGJlZm9yZSB0aGUgbGFzdCBjb2x1bW4gIHdvdWxkIGJlIGBsYXN0X2NvbChvZmZzZXQgPSAyKWAgYW5kIHRoZW4gdGhlIGA6YCBzeW1ib2wgaXMgaW50ZXJwcmV0ZWQgYXMgdGhyb3VnaCwgdGh1cyB3ZSBhcmUgc2VsZWN0aW5nIGZvciB0aGUgdGhpcmQgdG8gbGFzdCBjb2x1bW4gdGhyb3VnaCB0aGUgbGFzdCBjb2x1bW4gd2l0aCBgbGFzdF9jb2wob2Zmc2V0ID0gMik6bGFzdF9jb2woKWAuDQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwICU+JSANCiAgc2VsZWN0KHRpZHlyOjpsYXN0X2NvbChvZmZzZXQgPSAyKTpsYXN0X2NvbCgpKSAlPiUgDQogIHNsaWNlX2hlYWQobiA9IDQpDQpgYGANCg0KR3JlYXQhIFRoYXQgbG9va3MgbGlrZSB3ZSBleHBlY3RlZC4NCg0KRmluYWxseSwgd2Ugd2lsbCBzYXZlIG91ciB3cmFuZ2xlZCBkYXRhLCBhZ2FpbiB1c2luZyBgc2F2ZSgpYCBhbmQgd2Ugd2lsbCBhbHNvIHdyaXRlIHRvIGEgQ1NWIGZpbGUgdXNpbmcgYHdyaXRlX2NzdigpYC4NCmBgYHtyLCBldmFsID0gRkFMU0V9DQoNCnNhdmUoc2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwLCANCiAgICAgICAgICBmaWxlID0gaGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsDQogICAgICAgICAgICAgICAgICAgICAgInNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcC5yZGEiKSkNCg0Kd3JpdGVfY3N2KHNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcCwgDQogICAgICAgICAgZmlsZSA9IGhlcmUoImRhdGEiLCAid3JhbmdsZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICJzaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXAuY3N2IikpDQpgYGANCg0KDQojICoqRGF0YSBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbioqDQoqKiogDQoNCklmIHlvdSBoYXZlIGJlZW4gZm9sbG93aW5nIGFsb25nIGJ1dCBzdG9wcGVkLCB3ZSBjb3VsZCBsb2FkIG91ciBkYXRhIGxpa2Ugc286DQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsICJzaG9vdGluZ19kYXRhX3dyYW5nbGVkX3ByZV9tYXAucmRhIikpDQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwLnJkYSIpKQ0KDQpgYGANCg0KV2UgbmVlZCB0aGUgd2FuZ2xlZCBkYXRhIHRoYXQgaXMgcHJlcGFyZWQgYm90aCBmb3IgdGhlIG1hcCBhbmQgdGhlIGRhdGEganVzdCBiZWZvcmUgdGhlIGxhc3Qgd3JhbmdsaW5nIHN0ZXAgR2VvbWV0cnkgbGlzdHMgd2l0aCB0aGUgYHNmYCBwYWNrYWdlXSB0byBwcmVwYXJlIGZvciB0aGUgbWFwIGJlY2F1c2Ugd2UgcmVtb3ZlZCBzb21lIGV2ZW50cyB0aGF0IGRpZCBub3QgaGF2ZSBhZGRyZXNzZXMgdGhhdCB3ZXJlIGlkZW50aWZpZWQgYnkgR29vZ2xlIChoYWQgYE5BYCB2YWx1ZXMgZm9yIGxhdGl0dWRlIG9yIGxvbmdpdHVkZSkuICBXZSB3YW50IHRvIHVzZSBkYXRhIGZvciBhbGwgZXZlbnRzIGZvciBvdXIgc3RhdGlzdGljcywgdGFibGVzLCBhbmQgcGxvdHMuDQoNCiMjIyMgey5jbGlja190b19leHBhbmRfYmxvY2t9DQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIHByZXZpb3VzIHNlY3Rpb25zIGNsaWNrIGhlcmUuIDwvc3VtbWFyeT4NCg0KRmlyc3QgeW91IG5lZWQgdG8gaW5zdGFsbCB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiT0NTZGF0YSIpDQpgYGANCg0KVGhlbiwgeW91IG1heSBkb3dubG9hZCB0aGUgd3JhbmdsZWQgZGF0YSBgLnJkYWAgZmlsZXMgbGlrZSBzbzoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIGxpYnJhcnkoT0NTZGF0YSkNCndyYW5nbGVkX3JkYSgib2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkIiwgb3V0cGF0aCA9IGdldHdkKCkpDQojIGxvYWQoaGVyZTo6aGVyZSgiT0NTZGF0YSIsICJkYXRhIiwgIndyYW5nbGVkIiwgInNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcC5yZGEiKSkNCiMgbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwLnJkYSIpKQ0KYGBgDQoNClRvIGxvYWQgdGhlIGRvd25sb2FkZWQgZGF0YSBpbnRvIHlvdXIgZW52aXJvbm1lbnQsIHlvdSBtYXkgZG91YmxlIGNsaWNrIG9uIGVhY2ggb2YgdGhlIGAucmRhYCBmaWxlcyBpbiBSc3R1ZGlvIG9yIHVzaW5nIHRoZSBgbG9hZCgpYCBmdW5jdGlvbi4NCg0KSWYgdGhlIHBhY2thZ2UgZG9lcyBub3Qgd29yayBmb3IgeW91LCB0d28gUkRBIGZpbGVzIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC90cmVlL21hc3Rlci9kYXRhL3dyYW5nbGVkKSBvciBzbGlnaHRseSBtb3JlIGRpcmVjdGx5IFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC9tYXN0ZXIvZGF0YS93cmFuZ2xlZC9zaG9vdGluZ19kYXRhX3dyYW5nbGVkX2Zvcl9tYXAucmRhKSBhbmQgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkL21hc3Rlci9kYXRhL3dyYW5nbGVkL3Nob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcC5yZGEpLiBEb3dubG9hZCB0aGVzZSBmaWxlcyBhbmQgdGhlbiBwbGFjZSB0aGVtIGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gV2UgcmVjb21tZW5kIHVzaW5nIGFuIFJTdHVkaW8gcHJvamVjdCBhbmQgdGhlIFtgaGVyZWAgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBuYXZpZ2F0ZSB0byB5b3VyIGZpbGVzIG1vcmUgZWFzaWx5LiANCg0KV2UgaGF2ZSBwdXQgdGhlc2UgZmlsZXMgaW4gYSBkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgd2l0aGluIG91ciB3b3JraW5nIGRpcmVjdG9yeSAod2hpY2ggaGFzIGEgLlJwcm9qIGZpbGUpLg0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsICJzaG9vdGluZ19kYXRhX3dyYW5nbGVkX3ByZV9tYXAucmRhIikpDQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwLnJkYSIpKQ0KDQpgYGANCg0KPGhyIHN0eWxlPSJoZWlnaHQ6MXB4O2JvcmRlcjpub25lO2NvbG9yOiMzMzM7YmFja2dyb3VuZC1jb2xvcjojMzMzOyIgLz4NCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgbW9yZSBhYm91dCBjcmVhdGluZyBuZXcgcHJvamVjdHMgaW4gUlN0dWRpby4gPC9zdW1tYXJ5Pg0KDQpZb3UgY2FuIGNyZWF0ZSBhIHByb2plY3QgYnkgZ29pbmcgdG8gdGhlIEZpbGUgbWVudSBvZiBSU3R1ZGlvIGxpa2Ugc286DQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk5ld19wcm9qZWN0LnBuZyIpKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyBkbyBzbyBieSBjbGlja2luZyB0aGUgcHJvamVjdCBidXR0b246DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwcm9qZWN0X2J1dHRvbi5wbmciKSkNCmBgYA0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpIHRvIGxlYXJuIG1vcmUgYWJvdXQgdXNpbmcgUlN0dWRpbyBwcm9qZWN0cy4gDQoNCjwvZGV0YWlscz4NCjxociBzdHlsZT0iaGVpZ2h0OjFweDtib3JkZXI6bm9uZTtjb2xvcjojMzMzO2JhY2tncm91bmQtY29sb3I6IzMzMzsiIC8+DQo8L2RldGFpbHM+DQoNCiMjIyMNCg0KTHVja2lseSwgb3VyIGRhdGEgaXMgYWxyZWFkeSBpbiBwcmV0dHkgZ29vZCBzaGFwZSwgYnV0IHdlIHdhbnQgdG8gbWFrZSBvdXIgZGF0YSBtb3JlIHVzZWZ1bCBmb3Igb3VyIGRhc2hib2FyZC4gDQoNCkxldCdzIHNob3J0ZW4gdGhlIG5hbWUgb2YgdGhlIGRhdGEgdGhhdCB3YXMgd3JhbmdsZWQgdXAgdG8gdGhlIGxhc3Qgc3RlcCBmb3IgdGhlIG1hcC4gV2Ugd2lsbCB1c2UgYHNob290aW5nX2RhdGFgLg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgPC1zaG9vdGluZ19kYXRhX3dyYW5nbGVkX3ByZV9tYXANCmBgYA0KDQpXZSB3aWxsIGFsc28gcmVuYW1lIHRoZSBkYXRhIHRoYXQgaXMgd3JhbmdsZWQgZm9yIHRoZSBtYXAgdG8gYSBzaG9ydGVyIG5hbWU6DQoNCmBgYHtyfQ0Kc2hvb3RpbmdfZGF0YV9mb3JfbWFwIDwtIHNob290aW5nX2RhdGFfd3JhbmdsZWRfZm9yX21hcA0KYGBgDQoNCkxldCdzIGRvdWJsZSBjaGVjayB0aGF0IG91ciBkYXRhIGlzIGV4cGVjdGVkOg0KDQpgYGB7cn0NCmRpbShzaG9vdGluZ19kYXRhKQ0KZGltKHNob290aW5nX2RhdGFfZm9yX21hcCkNCmBgYA0KDQpHcmVhdCwgbG9va3MgbGlrZSB3ZSBpbmRlZWQgaGF2ZSBtb3JlIHJvd3MgaW4gb3VyIGBzaG9vdGluZ19kYXRhYCBhcyB3ZSB3b3VsZCBleHBlY3QuIA0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBlbGVtZW50cyB3ZSB3b3VsZCBsaWtlIHRvIGluY2x1ZGUgaW4gb3VyIGRhc2hib2FyZC4gDQoNCk9uZSB0aGluZyB3ZSB3b3VsZCBsaWtlIGlzIGFuIGludGVyYWN0aXZlIHRhYmxlLg0KDQojIyAqKkludGVyYWN0aXZlIFRhYmxlKioNCioqKg0KV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBkYXRhdGFibGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYERUYCBwYWNrYWdlLg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KRFQ6OmRhdGF0YWJsZShzaG9vdGluZ19kYXRhKQ0KYGBgDQpUaGlzIGNyZWF0ZXMgYSBzZWFyY2hhYmxlIHRhYmxlIGFuZCB0aGUgb3JkZXIgaW4gd2hpY2ggdGhlIGRhdGEgaXMgZGlzcGxheWVkIGNhbiBiZSB0b2dnbGVkIHRvIGNoYW5nZSBmb3IgZWFjaCB2YXJpYWJsZS4NCg0KSG93ZXZlciwgd2UgaGF2ZSBtYW55IHZhcmlhYmxlcyBvciBjb2x1bW5zIGluIG91ciBkYXRhc2V0LCBzbyB0aGlzIGNhbiBiZSBvdmVyd2hlbG1pbmcuIEluc3RlYWQgb2YgZGlzcGxheWluZyBhbGwgb2YgdGhlIHZhcmlhYmxlcywgbGV0J3MgY2hvb3NlIG9ubHkgc29tZSBvZiB0aGUgbW9zdCBpbnRlcmVzdGluZyB0byBkaXNwbGF5IGluIG91ciBkYXNoYm9hcmQuDQoNCmBgYHtyfQ0KRFRfdGFibGUgPC0gDQogIHNob290aW5nX2RhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoRGF0ZSwNCiAgICAgICAgICAgICAgICBTY2hvb2wsDQogICAgICAgICAgICAgICAgQ2l0eSwNCiAgICAgICAgICAgICAgICBTdGF0ZSwNCiAgICAgICAgICAgICAgICBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAsDQogICAgICAgICAgICAgICAgYE5hcnJhdGl2ZSAoRGV0YWlsZWQgU3VtbWFyeS8gQmFja2dyb3VuZClgKSAlPiUNCiAgcmVuYW1lKCJEZWF0aHMiID0gYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgcmVuYW1lKCJOYXJyYXRpdmUiID0gYE5hcnJhdGl2ZSAoRGV0YWlsZWQgU3VtbWFyeS8gQmFja2dyb3VuZClgKQ0KDQpEVDo6ZGF0YXRhYmxlKERUX3RhYmxlKQ0KYGBgDQoNCk5leHQsIHdlIHdpbGwgbWFrZSBzb21lIGRhdGEgdmlzdWFsaXphdGlvbnMuIA0KDQoNCiMjICoqWWVhcmx5IFNob290aW5ncyoqDQoqKioNCg0KV2Ugd291bGQgbGlrZSB0byBjcmVhdGUgYSBwbG90IG9mIHRoZSBudW1iZXIgb2Ygc2Nob29sIHNob290aW5ncyBwZXIgeWVhci4NCg0KVG8gZG8gdGhpcywgd2Ugd2lsbCBjb3VudCB0aGUgbnVtYmVyIG9mIHNjaG9vbCBzaG9vdGluZ3MgcGVyIHllYXIgdXNpbmcgdGhlIGBjb3VudCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UuIFdlIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIGNvdW50IHRoZSB1bmlxdWUgdmFsdWVzIG9mIHRoZSBgRGF0ZV95ZWFyYCB2YXJpYWJsZSBhbmQgbmFtZSB0aGUgbmV3IGNvbHVtbiBgU2hvb3RpbmdzYC4NCg0KDQpgYGB7cn0NCnNob290aW5nc19wZXJfeWVhciA8LQ0KICBzaG9vdGluZ19kYXRhICU+JQ0KICBjb3VudChEYXRlX3llYXIsIG5hbWUgPSAiU2hvb3RpbmdzIikNCg0Kc2hvb3RpbmdzX3Blcl95ZWFyDQpgYGANCg0KR29vZCwgdGhpcyBsb29rcyBhcyBleHBlY3RlZC4NCg0KTm93IHRvIG1ha2UgYSBwbG90IG9mIHRoaXMgZGF0YSB3ZSB3aWxsIHVzZSB0aGUgYGdncGxvdDJgIHBhY2thZ2UuDQoNCiMjIyMgey5jbGlja190b19leHBhbmRfYmxvY2t9DQoNCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayBoZXJlIGZvciBhbiBpbnRyb2R1Y3Rpb24gdG8gYGdncGxvdDJgLiA8L3N1bW1hcnk+DQoNClRoZSBbZ2dwbG90MiBwYWNrYWdlXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnKSBpcyBnZW5lcmFsbHkgaW50dWl0aXZlIGZvciBiZWdpbm5lcnMgYmVjYXVzZSBpdCBpcyBiYXNlZCBvbiBhICBbZ3JhbW1hciBvZiBncmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIuaHRtbCkgb3IgdGhlIGBnZ2AgaW4gYGdncGxvdDJgLiANClRoZSBpZGVhIGlzIHRoYXQgeW91IGNhbiBjb25zdHJ1Y3QgbWFueSBzZW50ZW5jZXMgYnkgbGVhcm5pbmcganVzdCBhIGZldyBub3VucywgYWRqZWN0aXZlcywgYW5kIHZlcmJzLiBUaGVyZSBhcmUgc3BlY2lmaWMg4oCcd29yZHPigJ0gdGhhdCB3ZSB3aWxsIG5lZWQgdG8gbGVhcm4gYW5kIG9uY2Ugd2UgZG8sIHlvdSB3aWxsIGJlIGFibGUgdG8gY3JlYXRlIChvciDigJx3cml0ZeKAnSkgaHVuZHJlZHMgb2YgZGlmZmVyZW50IHBsb3RzLg0KDQpUaGUgY3JpdGljYWwgcGFydCB0byBtYWtpbmcgZ3JhcGhpY3MgdXNpbmcgYGdncGxvdDJgIGlzIHRoZSBkYXRhIG5lZWRzIHRvIGJlIGluIGEgX3RpZHlfIGZvcm1hdC4gDQpHaXZlbiB0aGF0IHdlIGhhdmUganVzdCBzcGVudCB0aW1lIHB1dHRpbmcgb3VyIGRhdGEgaW4gX3RpZHlfIGZvcm1hdCwgd2UgYXJlIHByaW1lZCB0byB0YWtlIGFkdmFudGFnZSBvZiBhbGwgdGhhdCBgZ2dwbG90MmAgaGFzIHRvIG9mZmVyISANCg0KV2Ugd2lsbCBzaG93IGhvdyBpdCBpcyBlYXN5IHRvIHBpcGUgX3RpZHlfIGRhdGEgKG91dHB1dCkgYXMgaW5wdXQgdG8gb3RoZXIgZnVuY3Rpb25zIHRoYXQgY3JlYXRlIHBsb3RzLiANClRoaXMgYWxsIHdvcmtzIGJlY2F1c2Ugd2UgYXJlIHdvcmtpbmcgDQp3aXRoaW4gdGhlIF90aWR5dmVyc2VfLiANCg0KKipXaGF0IGlzIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uPyoqIA0KQXMgZXhwbGFpbmVkIGJ5IEhhZGxleSBXaWNraGFtOg0KDQo+IFRoZSBncmFtbWFyIHRlbGxzIHVzIHRoYXQgYSBzdGF0aXN0aWNhbCBncmFwaGljIGlzIGEgbWFwcGluZyBmcm9tIGRhdGEgdG8gYWVzdGhldGljIGF0dHJpYnV0ZXMgKGNvbG91ciwgc2hhcGUsIHNpemUpIG9mIGdlb21ldHJpYyBvYmplY3RzIChwb2ludHMsIGxpbmVzLCBiYXJzKS4gVGhlIHBsb3QgbWF5IGFsc28gY29udGFpbiBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGRhdGEgYW5kIGlzIGRyYXduIG9uIGEgc3BlY2lmaWMgY29vcmRpbmF0ZXMgc3lzdGVtLg0KDQpgZ2dwbG90MmAgVGVybWlub2xvZ3k6IA0KDQotICoqZ2dwbG90KiogLSB0aGUgbWFpbiBmdW5jdGlvbiB3aGVyZSB5b3Ugc3BlY2lmeSB0aGUgZGF0YXNldCBhbmQgdmFyaWFibGVzIHRvIHBsb3QgKHRoaXMgaXMgd2hlcmUgd2UgZGVmaW5lIHRoZSBgeGAgYW5kDQpgeWAgdmFyaWFibGUgbmFtZXMpDQotICoqZ2VvbXMqKiAtIGdlb21ldHJpYyBvYmplY3RzDQogICAgLSBlLmcuIGBnZW9tX3BvaW50KClgLCBgZ2VvbV9iYXIoKWAsIGBnZW9tX2xpbmUoKWAsIGBnZW9tX2hpc3RvZ3JhbSgpYA0KLSAqKmFlcyoqIC0gYWVzdGhldGljcw0KICAgIC0gc2hhcGUsIHRyYW5zcGFyZW5jeSwgY29sb3IsIGZpbGwsIGxpbmUgdHlwZXMNCi0gKipzY2FsZXMqKiAtIGRlZmluZSBob3cgeW91ciBkYXRhIHdpbGwgYmUgcGxvdHRlZA0KICAgIC0gY29udGludW91cywgZGlzY3JldGUsIGxvZywgZXRjDQoNClRoZSBmdW5jdGlvbiBgYWVzKClgIGlzIGFuIGFlc3RoZXRpYyBtYXBwaW5nIGZ1bmN0aW9uIGluc2lkZSB0aGUgYGdncGxvdCgpYCBvYmplY3QuIA0KV2UgdXNlIHRoaXMgZnVuY3Rpb24gdG8gc3BlY2lmeSBwbG90IGF0dHJpYnV0ZXMgKGUuZy4gYHhgIGFuZCBgeWAgdmFyaWFibGUgbmFtZXMpIHRoYXQgd2lsbCBub3QgY2hhbmdlIGFzIHdlIGFkZCBtb3JlIGxheWVycy4gIA0KDQpBbnl0aGluZyB0aGF0IGdvZXMgaW4gdGhlIGBnZ3Bsb3QoKWAgb2JqZWN0IGJlY29tZXMgYSBnbG9iYWwgc2V0dGluZy4gDQpGcm9tIHRoZXJlLCB3ZSB1c2UgdGhlIGBnZW9tYCBvYmplY3RzIHRvIGFkZCBtb3JlIGxheWVycyB0byB0aGUgYmFzZSBgZ2dwbG90KClgIG9iamVjdC4gDQpUaGVzZSB3aWxsIGRlZmluZSB3aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGlsbHVzdHJhdGluZyB1c2luZyB0aGUgZGF0YS4NCg0KPC9kZXRhaWxzPg0KDQojIyMjDQoNCioqKg0KRm9yIG1vcmUgb2YgYW4gaW50cm9kdWN0aW9uIG9uIGNyZWF0aW5nIHBsb3RzIHdpdGggYGdncGxvdDJgICwgc2VlIHRoaXMgW2Nhc2Ugc3R1ZHldKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtYnAtY28yLWVtaXNzaW9ucy8pDQoNCioqKg0KDQpGaXJzdCwgd2Ugc3RhcnQgd2l0aCB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4NCg0KVGhpcyBmdW5jdGlvbiByZXF1aXJlcyB0aGF0IHRoZSBhZXN0aGV0aWNzIGBhZXMoKWAgYmUgc3BlY2lmaWVkLiBUaGlzIGludm9sdmVzIGNob29zaW5nIHdoYXQgdmFyaWFibGUgd2lsbCBiZSBwbG90dGVkIG9uIHRoZSB4LWF4aXMgYW5kIHRoZSB5LWF4aXMuIA0KDQpgYGB7cn0NCnNob290aW5nc19wZXJfeWVhciAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSBEYXRlX3llYXIsIHkgPSBTaG9vdGluZ3MpKQ0KYGBgDQoNClVzaW5nIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uIGFsb25lIHdpbGwgY3JlYXRlIGFuIGVtcHR5IHBsb3QgYXJlYS4gVG8gbWFrZSBvdXIgcGxvdCBub3QgZW1wdHksIHdlIG5lZWQgdG8gc2VsZWN0IG9uZSBvZiB0aGUgYGdlb21fKmAgZnVuY3Rpb25zIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZSB0byBzcGVjaWZ5IHdoYXQgdHlwZSBvZiBwbG90IHdlIHdhbnQgdG8gY3JlYXRlLg0KDQpBc3N1bWluZyB0aGUgYGdncGxvdDJgIGxpYnJhcnkgaXMgbG9hZGVkLCB0eXBlIGBnZW9tYCBpbnRvIHRoZSBSU3R1ZGlvIGNvbnNvbGUgYW5kIHlvdSB3aWxsIHNlZSBtYW55IG9wdGlvbnMgdG8gc2Nyb2xsIHRocm91Z2guDQoNCkhlcmUsIHdlIHVzZSBhIGBnZW9tX2NvbCgpYCBwbG90LCB3aGljaCBpcyBhIHBhcnRpY3VsYXIgdHlwZSBvZiBiYXIgcGxvdCB0aGF0IHVzZXMgdGhlIGFjdHVhbCB2YWx1ZXMgdG8gcGxvdCwgcmF0aGVyIHRoYW4gY291bnRzLCB3aGljaCBpcyB0aGUgZGVmYXVsdCBvZiBgZ2VvbV9iYXIoKWAuIFdlIHdpbGwgc3BlY2lmeSB3aXRoIHRoZSBgZmlsbGAgYXJndW1lbnQsIHRoYXQgd2Ugd2FudCBvdXIgYmFycyB0byBiZSBmaWxsZWQgd2l0aCB0aGUgY29sb3IgYmxhY2suDQoNCmBgYHtyfQ0Kc2hvb3RpbmdzX3Blcl95ZWFyICU+JQ0KICAgIGdncGxvdChhZXMoeCA9IERhdGVfeWVhciwgeSA9IFNob290aW5ncykpICsNCiAgICBnZW9tX2NvbChmaWxsID0gImJsYWNrIikNCmBgYA0KDQpXZSBhbHNvIG1vZGlmeSB0aGUgeC1heGlzIHVzaW5nIHRoZSBgc2NhbGVfeF9jb250aW51b3VzKClgIGZ1bmN0aW9uLiBUaGlzIGZ1bmN0aW9uIGFsbG93cyBmb3Igc3BlY2lmaWNhdGlvbiBvZiB0aGUgcmFuZ2Ugb3IgbGltaXRzIG9mIHRoZSBheGlzIHVzaW5nIHRoZSBgbGltaXRzYCBhcmd1bWVudC4gV2UgY2FuIHVzZSB0aGUgYmFzZSBgc2VxKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIHNlcXVlbmNlIG9mIG51bWJlcnMgZm9yIGVhY2ggdGljayBtYXJrLg0KDQpXZSBjYW4gYWRkIGxhYmVscyB0byBvdXIgcGxvdCB1c2luZyB0aGUgYGxhYnMoKWAgZnVuY3Rpb24gZnJvbSBgZ2dwbG90MmAuIFRoaXMgaGFzIGFyZ3VtZW50cyBzdWNoIGFzIGB4YCBhbmQgYHlgIGZvciB0aGUgYXhlcyBhbmQgYHRpdGxlYCBhbmQgYHN1YnRpdGxlYCBmb3IgdGl0bGVzLiBXZSBjYW4gdXNlIGBOVUxMYCB0byByZW1vdmUgYSBsYWJlbC4gRm9yIGV4YW1wbGUgdG8gcmVtb3ZlIHRoZSB4LWF4aXMgbGFiZWwgd2UgY2FuIHVzZSBgeCA9IE5VTExgDQoNCldlIHdpbGwgYWxzbyBtb2RpZnkgdGhlIG92ZXJhbGwgYWVzdGhldGljcyBvZiB0aGUgcGxvdCB1c2luZyBhIGB0aGVtZV8qYCBmdW5jdGlvbi4gU2VlIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2d0aGVtZS5odG1sKSBmb3IgYSBsaXN0IG9mIG9wdGlvbnMuDQoNCmBgYHtyfQ0Kc3RhcnQgPC0gMTk3MA0KZW5kIDwtIDIwMjANCg0Kc2hvb3RpbmdzX3Blcl95ZWFyX3AgPC0gDQogIHNob290aW5nc19wZXJfeWVhciAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSBEYXRlX3llYXIsIHkgPSBTaG9vdGluZ3MpKSArDQogICAgICBnZW9tX2NvbChmaWxsID0gImJsYWNrIikgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoc3RhcnQtMSwgZW5kKzEpKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgbGFicyh0aXRsZSA9ICJZZWFybHkgU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICAgIHN1YnRpdGxlID0gIlVuaXRlZCBTdGF0ZXMiLA0KICAgICAgICAgICB4ID0gTlVMTCwNCiAgICAgICAgICAgeSA9ICJTY2hvb2wgU2hvb3RpbmdzIikNCg0Kc2hvb3RpbmdzX3Blcl95ZWFyX3AgDQpgYGANCg0KIyMgKipZZWFybHkgRGVhdGhzKioNCioqKg0KDQpMZXQncyBtYWtlIGEgc2ltaWxhciBwbG90IGZvciB0aGUgbnVtYmVyIG9mIGRlYXRocw0KDQpgYGB7cn0NCmRlYXRoc19wZXJfeWVhcjwtDQogIHNob290aW5nX2RhdGEgJT4lIA0KICBncm91cF9ieShEYXRlX3llYXIpICU+JQ0KICBzdW1tYXJpemUoRGVhdGhzID0gc3VtKGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCkpDQoNCmRlYXRoc19wZXJfeWVhcl9wIDwtIA0KICBkZWF0aHNfcGVyX3llYXIgJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZV95ZWFyLCB5ID0gRGVhdGhzKSkgKw0KICAgICAgZ2VvbV9jb2woZmlsbCA9ICJibGFjayIpICsNCiAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKHN0YXJ0LTEsIGVuZCsxKSkgKw0KICAgICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICAgIGxhYnModGl0bGUgPSAiWWVhcmx5IERlYXRocyBBdHRyaWJ1dGFibGUgdG8gU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICAgIHN1YnRpdGxlID0gIlVuaXRlZCBTdGF0ZXMiLA0KICAgICAgICAgICB4ID0gTlVMTCkNCg0KZGVhdGhzX3Blcl95ZWFyX3ANCmBgYA0KDQoqKk5vdGUqKjogV2hlbiB1c2luZyB0aGUgYHN1bW1hcml6ZSgpYCBmdW5jdGlvbiwgd2UgZG9uJ3QgbmVlZCB0byB1c2UgdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24gaGVyZS4NCg0KTmV4dCwgZm9yIHRoZSBwdXJwb3NlcyBvZiB0aGUgZGFzaGJvYXJkLCB3ZSBhY3R1YWxseSB3YW50IHRvIGNyZWF0ZSBqdXN0IG9uZSBwbG90IHRoYXQgc2hvd3MgYm90aCB0aGUgbnVtYmVyIG9mIHNjaG9vbCBzaG9vdGluZ3MgcGVyIHllYXIgYW5kIHRoZSBudW1iZXIgb2YgZGVhdGhzLg0KDQpXZSBjYW4gZG8gc28gYnkgY29tYmluaW5nIG91ciBgc2hvb3RpbmdzX3Blcl95ZWFyYCBhbmQgIGBkZWF0aHNfcGVyX3llYXJgIHRpYmJsZXMgdG9nZXRoZXIgYW5kIG1ha2luZyB3aGF0IGlzIGNhbGxlZCBhIGZhY2V0ZWQgcGxvdCwgdXNpbmcgdGhlIGBmYWNldF93cmFwKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSB0d28gcGxvdHMgbmV4dCB0byBvbmUgYW5vdGhlci4NCg0KVG8gY29tYmluZSBvdXIgZGF0YSB3ZSB3aWxsIHVzZSB0aGUgYGZ1bGxfam9pbigpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UuIFRoaXMgbWFpbnRhaW5zIGFsbCB2YWx1ZXMgZnJvbSBib3RoIHRpYmJsZXMuDQoNClRvIGRvIHNvIHdlIHdpbGwgYmUgbWFraW5nIG91ciB0YWJsZSAibG9uZ2VyIiwgbWVhbmluZyB0aGF0IGl0IHdpbGwgaGF2ZSBmZXdlciBjb2x1bW5zIGFuZCBtb3JlIHJvd3MuIA0KU2VlIFtoZXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XaWRlX2FuZF9uYXJyb3dfZGF0YSkgZm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgZGlmZmVyZW50IHRhYmxlIGZvcm1hdHMsIHR5cGljYWxseSByZWZlcnJlZCB0byBhcyB3aWRlIGFuZCBsb25nIG9yIHNvbWV0aW1lcyBuYXJyb3cuDQoNCldlIHdpbGwgdXNlIHRoZSBgcGl2b3RfbG9uZ2VyKClgIGZ1bmN0aW9uIGZyb20gdGhlIGB0aWR5cmAgcGFja2FnZSB0byBjaGFuZ2UgdGhlIHNoYXBlIG9mIG91ciB0YWJsZS4gDQoNClRoZXJlIGFyZSAzIG1haW4gYXJndW1lbnRzIGluIHRoaXMgZnVuY3Rpb246ICAgDQoNCjEuIGBjb2xzYCAtIHdoaWNoIHNwZWNpZmllcyB3aGF0IGNvbHVtbnMgdG8gY29sbGFwc2UgIA0KMi4gYG5hbWVzX3RvYCAtIHdoaWNoIHNwZWNpZmllcyB0aGUgbmFtZSBvZiB0aGUgbmV3IGNvbHVtbiB0aGF0IHdpbGwgYmUgY3JlYXRlZCB0aGF0IHdpbGwgY29udGFpbiB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBjb2x1bW5zIHlvdSBhcmUgY29sbGFwc2luZyAgDQozLiBgdmFsdWVzX3RvYCAtIHdoaWNoIHNwZWNpZmllcyB0aGUgbmFtZSBvZiB0aGUgbmV3IGNvbHVtbiB0aGF0IHdpbGwgYmUgY3JlYXRlZCB0aGF0IHdpbGwgY29udGFpbiB0aGUgdmFsdWVzIGZyb20gdGhlIGNvbHVtbnMgeW91IGFyZSBjb2xsYXBzaW5nIA0KDQpUbyBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0byBjb2xsYXBzZSBhbGwgdGhlIGNvbHVtbnMgdGhhdCBoYXZlIHllYXIgdmFsdWVzLCB3ZSBjYW4gY2hvb3NlIGFsbCB0aG9zZSBleGNlcHQgdGhlIGBEYXRlX3llYXJgIHZhcmlhYmxlIGJ5IHVzaW5nIHRoZSBgLWAgbmVnYXRpdmUgb3BlcmF0b3IuIA0KDQpgYGB7cn0NCnBlcl95ZWFyIDwtIA0KICBmdWxsX2pvaW4oc2hvb3RpbmdzX3Blcl95ZWFyLCBkZWF0aHNfcGVyX3llYXIpDQoNCnBlcl95ZWFyICU8PiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtRGF0ZV95ZWFyLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJldmVudHMiLCANCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImlkIikNCg0KcGVyX3llYXINCmBgYA0KDQpIbW1tLCB3ZSBzZWUgdGhlIGRhdGEgdHlwZSBvZiB0aGUgYGlkYCBjb2x1bW4gaXMgYSBjaGFyYWN0ZXIgKGA8Y2hyPmApLiBMZXQncyBjb252ZXJ0IGl0IHRvIGEgZmFjdG9yLCBzbyB0aGF0IHRoZSBvcmRlciBpbiB3aGljaCBgU2hvb3RpbmdzYCBhbmQgYERlYXRoc2AgYXBwZWFyIGlzIHRoZSBvcmRlciBpbiB3aGljaCB0aGV5IGFwcGVhciBmaXJzdCByYXRoZXIgdGhhbiBieSBhbHBoYWJldGljYWwgb3JkZXIgKHdoaWNoIGlzIGRlZmF1bHQpLg0KDQpVc2luZyB0aGUgYGZjdF9pbm9yZGVyKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBmb3JjYXRzYCBwYWNrYWdlLCB3ZSBjYW4gZWFzaWx5IHJlb3JkZXIgdGhlIGBpZGAgdmFyaWFibGVgLiANCg0KYGBge3J9DQpwZXJfeWVhciAlPD4lIA0KICBtdXRhdGUoaWQgPSBmb3JjYXRzOjpmY3RfaW5vcmRlcihpZCkpDQoNCnBlcl95ZWFyDQpgYGANCg0KTm93IHNpbmNlIHdlIHRoZSBuZXcgdmFyaWFibGUgZm9yIHRoZSBuYW1lcyBpcyBjYWxsZWQgYGlkYCB3ZSB3aWxsIHVzZSB0aGlzIGFzIHRoZSB2YXJpYWJsZSB0byBjcmVhdGUgdGhlIGZhY2V0IGxpa2Ugc286IGBmYWNldF93cmFwKH5pZClgLiBXZSBjYW4gYWxzbyBzcGVjaWZ5IHRoYXQgd2Ugd2FudCBib3RoIHBsb3RzIHRvIGhhdmUgdGhlaXIgb3duIHktYXhpcyB3aXRoIHRoZSBgc2NhbGVzID0gImZyZWUiYCBhcmd1bWVudC4gVGhpcyBjYXVzZXMgZWFjaCB0byBoYXZlIHRoZSB5LWF4aXMgYXV0b21hdGljYWxseSBzY2FsZWQgZm9yIHRoZSBkYXRhIGluIGVhY2ggcGxvdC4gV2UgY2FuIHRoZW4gdXNlIHRoZSAgYHNjYWxlX3lfY29udGludW91cygpYCBmdW5jdGlvbiB0byBzZXQgYm90aCBvZiB0aGUgeS1heGVzIHRvIGJlIHRoZSBzYW1lLiANCg0KYGBge3J9DQpwZXJfeWVhciAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gRGF0ZV95ZWFyLCB5ID0gZXZlbnRzLCBmaWxsID1pZCkpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBmYWNldF93cmFwKH5pZCwgc2NhbGVzID0gImZyZWUiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhzdGFydC0xLCBlbmQrMSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEyMCwgYnkgPSAzMCksDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgwLCAxMjAsIGJ5ID0gMzApLA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEyMSkpKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJZZWFybHkgU2hvb3RpbmdzIGFuZCBEZWF0aHMgQXR0cmlidXRhYmxlIHRvIFNjaG9vbCBTaG9vdGluZ3MiLA0KICAgICAgICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICB5ID0gIk51bWJlciBvZiBldmVudHMiLA0KICAgICAgICAgeCA9ICJZZWFyIikrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiYmxhY2siKSkrDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCANCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9ZWxlbWVudF9yZWN0KGZpbGw9ImNvcm5mbG93ZXJibHVlIiksDQogICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAnd2hpdGUnLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTQpKQ0KYGBgDQoNCk5leHQsIHdlIGNhbiBtb2RpZnkgdGhlIHBsb3QgZnVydGhlciBzbyB0aGF0IGl0IGlzIG1vcmUgb2J2aW91cyB3aGF0IGVhY2ggcGxvdCBpcyBzaG93aW5nLiBXZSBjYW4gdXBkYXRlIHRoZSBuYW1lcyBvZiB0aGUgeS1heGlzIGZvciBlYWNoIHBsb3QgYnkgY2hhbmdpbmcgdGhlIGBzdHJpcC5wb3NpdGlvbmAgYXJndW1lbnQgb2YgdGhlIGBmYWNldF93cmFwKClgIGZ1bmN0aW9uIHRvIGJlIHBsYWNlZCBvbiB0aGUgbGVmdCByYXRoZXIgdGhhbiBhYm92ZS4gQ3VycmVudGx5IGl0IGlzIHRoZSBsYWJlbCBpbiBibHVlIHRoYXQgc2F5cyB3aGF0IHRoZSB2YWx1ZSBvZiB0aGUgYGlkYCB2YXJpYWJsZSBpcyBmb3IgZWFjaCBwbG90LiBUaGlzIGFsc28gcmVxdWlyZXMgc29tZSBtb2RpZmljYXRpb24gb2YgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiB0byBwbGFjZSB0aGUgYHN0cmlwLnRleHRgIG91dHNpZGUgdGhlIHBsb3QgYXJlYSBhbmQgdG8gcmVtb3ZlIHRoZSBiYWNrZ3JvdW5kLkZ1cnRoZXJtb3JlLCB3ZSBhbHNvIGNoYW5nZSB0aGUgdGV4dCB1c2luZyB0aGUgYGxhYmVsbGVyYCBhcmd1bWVudCBvZiB0aGUgYGZhY2V0X3dyYXAoKWAgZnVuY3Rpb24uIFRoZSBgYXNfbGFiZWxsZXIoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGdncGxvdDJgIHBhY2thZ2UgY2FuIGNoYW5nZSBvdXQgdGhlIGBpZGAgdmFsdWVzIGZvciBvdGhlciB0ZXh0IGxpa2UgaW4gdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7cn0NCnBlcl95ZWFyICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBEYXRlX3llYXIsIHkgPSBldmVudHMsIGZpbGwgPWlkKSkgKw0KICAgIGdlb21fY29sKCkgKw0KICAgIGZhY2V0X3dyYXAofmlkLCANCiAgICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIiwgDQogICAgICAgICAgICAgICBsYWJlbGxlciA9IGFzX2xhYmVsbGVyKGMoU2hvb3RpbmdzID0gIlNob290aW5ncyAoIyBvZiBldmVudHMpIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVhdGhzID0gIkRlYXRocyAoIyBvZiBwZW9wbGUpIikpLCANCiAgICAgICAgICAgICAgIHN0cmlwLnBvc2l0aW9uID0gImxlZnQiKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhzdGFydC0xLCBlbmQrMSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEyMCwgYnkgPSAzMCksDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgwLCAxMjAsIGJ5ID0gMzApLA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEyMSkpKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJZZWFybHkgU2hvb3RpbmdzIGFuZCBEZWF0aHMgQXR0cmlidXRhYmxlIHRvIFNjaG9vbCBTaG9vdGluZ3MiLA0KICAgICAgICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgIHggPSAiWWVhciIpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsYWNrIikpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgDQogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiLA0KICAgICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkNCmBgYA0KDQpHb29kLCAgTm93IHRoaXMgaXMgbXVjaCBlYXNpZXIgdG8gaW50ZXJwcmV0Lg0KDQpPdXIgbGFzdCBzdGVwIGluIHRoaXMgc2VjdGlvbiBpcyB0byBzYXZlIHRoZSBzdHlsZSBzZXR0aW5ncyBvZiB0aGlzIHBsb3QgYXMgdGhlbWUgc28gd2UgY2FuIHJldXNlIGl0IGZvciBmdXR1cmUgcGxvdHMuIFRvIGRvIHRoaXMsIHdlIHVzZSB0aGUgYmFzZSBgZnVuY3Rpb24oKWAgZnVuY3Rpb246DQoNCmBgYHtyfQ0KdGhlbWVfZGFzaGJvYXJkIDwtIGZ1bmN0aW9uKCl7IA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwNCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiLA0KICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkpDQp9DQpgYGANCg0KDQoNCiMjICoqWWVhcmx5IEN1bXVsYXRpdmUgU2hvb3RpbmdzKioNCioqKg0KDQpOb3cgbGV0J3MgbWFrZSBhbm90aGVyIHBsb3Qgb2YgdGhlIGN1bXVsYXRpdmUgZGVhdGhzIGVhY2ggeWVhciBpbmNsdWRpbmcgdGhvc2Ugb2YgdGhlIHByZXZpb3VzIHllYXJzLiBJbiB0aGlzIGNhc2Ugd2UgY2FuIHVzZSB0aGUgYHNob290aW5nc19wZXJfeWVhcmAgb2JqZWN0IHRoYXQgd2UgcHJldmlvdXNseSBtYWRlLg0KDQpgYGB7cn0NCnNob290aW5nc19wZXJfeWVhcg0KYGBgDQoNCkhvd2V2ZXIsIHdlIHdhbnQgdG8gYWRkIGEgbmV3IHZhcmlhYmxlIHVzaW5nIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiBjYWxsZWQgYG5fY3VtX3N1bWAgYnkgdXNpbmcgdGhlIGBjdW1zdW0oKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGEgY3VtdWxhdGl2ZSBzdW0gYmFzZWQgb24gdGhlIHllYXJseSBjb3VudC4gDQoNCmBgYHtyfQ0Kc2hvb3RpbmdzX3Blcl95ZWFyX2N1bSA8LSBzaG9vdGluZ3NfcGVyX3llYXIgJT4lDQogICAgbXV0YXRlKFNob290aW5ncyA9IGN1bXN1bShTaG9vdGluZ3MpKQ0KDQpkZWF0aHNfcGVyX3llYXJfY3VtIDwtIGRlYXRoc19wZXJfeWVhciAlPiUNCiAgICBtdXRhdGUoRGVhdGhzID0gY3Vtc3VtKERlYXRocykpDQoNCnNob290aW5nc19wZXJfeWVhcl9jdW0NCmBgYA0KDQpOZXh0LCB3ZSBqb2luIHRoZXNlIHRhYmxlcyB0b2dldGhlcg0KDQpgYGB7cn0NCnBlcl95ZWFyX2N1bSA8LSANCiAgZnVsbF9qb2luKHNob290aW5nc19wZXJfeWVhcl9jdW0sIGRlYXRoc19wZXJfeWVhcl9jdW0pDQoNCnBlcl95ZWFyX2N1bSAlPD4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoU2hvb3RpbmdzLERlYXRocyApLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJldmVudHMiLCANCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImlkIikNCg0KcGVyX3llYXJfY3VtDQpgYGANCg0KR29vZCwgdGhpcyBsb29rcyBsaWtlIHdlIHdvdWxkIGV4cGVjdC4NCg0KTm93IGxldCdzIG1ha2UgYSBwbG90IGxpa2Ugd2UgZGlkIGJlZm9yZToNCg0KYGBge3J9DQpwZXJfeWVhcl9jdW0gJTw+JSANCiAgbXV0YXRlKGlkID0gZm9yY2F0czo6ZmN0X2lub3JkZXIoaWQpKQ0KDQpwZXJfeWVhcl9jdW0gJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZV95ZWFyLCB5ID0gZXZlbnRzLCBmaWxsID0gaWQpKSArDQogICAgICBnZW9tX2NvbCgpICsNCiAgICAgIGZhY2V0X3dyYXAofmlkLCBzY2FsZXMgPSAiZnJlZSIsIA0KICAgICAgICAgICAgICAgICBsYWJlbGxlciA9IGFzX2xhYmVsbGVyKGMoU2hvb3RpbmdzID0gIlNob290aW5ncyAoIyBvZiBldmVudHMpIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWF0aHMgPSAiRGVhdGhzICgjIG9mIHBlb3BsZSkiKSksIA0KICAgICAgICAgICAgICAgICBzdHJpcC5wb3NpdGlvbiA9ICJsZWZ0IikgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoc3RhcnQtMSwgZW5kKzEpKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDE1MDAsIGJ5ID0gNTAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoMCwgMTUwMCwgYnkgPSA1MDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMTUwMCkpICsNCiAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICBsYWJzKHRpdGxlID0gIkN1bXVsYXRpdmUgWWVhcmx5IFNob290aW5ncyBhbmQgRGVhdGhzXG5BdHRyaWJ1dGFibGUgdG8gU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICAgIHN1YnRpdGxlID0gIlVuaXRlZCBTdGF0ZXMiLA0KICAgICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgICAgeCA9ICJZZWFyIikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiYmxhY2siKSkgKw0KICAgICAgdGhlbWVfZGFzaGJvYXJkKCkNCmBgYA0KDQoqKk5vdGUqKjogdGhlIGxpbWl0cyBmb3IgdGhlIHktYXhpcyB3ZXJlIGRldGVybWluZWQgYnkgZmlyc3QgcGxvdHRpbmcgd2l0aG91dCB0aGUgYHNjYWxlX3lfY29udGludW91cygpYCBmdW5jdGlvbi4NCg0KDQoNCiMjICoqRGVhdGhzIHBlciBTaG9vdGluZyoqDQoqKioNCg0KTmV4dCwgd2Ugd2lsbCBtYWtlIGEgcGxvdCBvZiB0aGUgbnVtYmVyIG9mIGRlYXRocyBwZXIgc2Nob29sIHNob290aW5nIGJhc2VkIG9uIHRoZSBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAgdmFyaWFibGUuIA0KDQojIyMjIHsucmVjYWxsX2NvZGVfcXVlc3Rpb25fYmxvY2t9DQoNCjxiPjx1PiBRdWVzdGlvbiBPcHBvcnR1bml0eSA8L3U+PC9iPg0KDQpTZWUgaWYgeW91IGNhbiBjb21lIHVwIHdpdGggdGhlIGNvZGUgZm9yIHRoZSBwbG90Lg0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgYW5zd2VyLiA8L3N1bW1hcnk+DQoNCg0KYGBge3J9DQpkZWF0aHNfcGVyX2V2ZW50IDwtDQogIHNob290aW5nX2RhdGEgJT4lDQogIGdyb3VwX2J5KGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCkgJT4lDQogIGNvdW50KCkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpwZXJfc2hvb3RpbmdfcGxvdCA8LWRlYXRoc19wZXJfZXZlbnQgJT4lDQogIGdncGxvdChhZXMoeSA9IGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCwgeCA9IG4pKSArDQogICAgZ2VvbV9jb2woZmlsbCA9ICJibGFjayIpKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJEZWF0aHMgcGVyIFNjaG9vbCBTaG9vdGluZyIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJVbml0ZWQgU3RhdGVzIiwNCiAgICAgICAgIHggPSAiU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICB5ID0gIiIpDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KIyMjIw0KDQpgYGB7cn0NCnBlcl9zaG9vdGluZ19wbG90IA0KYGBgDQoNClRoaXMgcGxvdCBjb3VsZCBhbHNvIGhhdmUgYmVlbiBtYWRlIHVzaW5nIGBnZW9tX2JhcigpYCBpbnN0ZWFkIG9mIGBnZW9tX2NvbCgpYCB0aGlzIG1ha2VzIGEgc2ltaWxhciBwbG90IGJ1dCBhdXRvbWF0aWNhbGx5IHVzZXMgdGhlIGNvdW50IGZvciBvbmUgb2YgdGhlIGF4ZXMsIHRodXMgaXQgaXMgbm90IHJlcXVpcmVkIHRvIGZpcnN0IHN1bW1hcml6ZSB0aGUgZGF0YSB1c2luZyB0aGUgYGNvdW50KClgIGZ1bmN0aW9uLiANCg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCkpICsNCiAgICBnZW9tX2JhcihmaWxsID0gImJsYWNrIikgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJEZWF0aHMgcGVyIFNjaG9vbCBTaG9vdGluZyIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJVbml0ZWQgU3RhdGVzIiwNCiAgICAgICAgIHggPSAiU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICB5ID0gIiIpDQpgYGANCg0KDQoNCkJlY2F1c2Ugb2YgdGhlIHNrZXdlZCBkaXN0cmlidXRpb24sIGl0IGlzIGRpZmZpY3VsdCB0byBzZWUgdGhlIHNjaG9vbCBzaG9vdGluZ3MgdGhhdCBoYWQgbW9yZSBudW1lcm91cyBkZWF0aHMsIHNvIHdlIHdpbGwgYWRkIGEgZmFjZXQgdGhhdCB6b29tcyBpbiBvbiB0aGlzIHBvcnRpb24gb2YgdGhlIHBsb3QuIFdlIGNhbiBkbyBzbywgdXNpbmcgdGhlIGBmYWNldF96b29tKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBnZ2ZvcmNlYCBwYWNrYWdlLg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCkpICsNCiAgICBnZW9tX2JhcihmaWxsID0gImJsYWNrIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KHB1bGwoc2hvb3RpbmdfZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApKSwgYnkgPSAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKDAsIG1heChwdWxsKHNob290aW5nX2RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApKSwgYnkgPSAxKSkgKw0KICAgIGdnZm9yY2U6OmZhY2V0X3pvb20oeGxpbSA9IGMoNCwgbWF4KHB1bGwoc2hvb3RpbmdfZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwyMCkpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGxhYnModGl0bGUgPSAiRGVhdGhzIHBlciBTY2hvb2wgU2hvb3RpbmciLA0KICAgICAgICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICB4ID0gIkRlYXRocyBwZXIgc2hvb3RpbmciLA0KICAgICAgICAgeSA9ICJOdW1iZXIgb2YgZXZlbnRzIHdpdGggZ2l2ZW4gbnVtYmVyIG9mIGRlYXRocyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpDQpgYGANCg0KSXQgaXMgc3RpbGwgZGlmZmljdWx0IHRvIHNlZS4gTGV0J3MgdHJ5IHNvbWUgb3RoZXIgb3B0aW9ucy4NCg0KVGhlIGBnZW9tX2ZyZXFwb2x5KClgIGZ1bmN0aW9uIGNyZWF0ZXMgYSBncmFwaCB0aGF0IG1ha2VzIGl0IHZlcnkgZWFzeSB0byBzZWUgdGhhdCBtb3N0IHNjaG9vbCBzaG9vdGluZ3MgcmVzdWx0IGluIHplcm8gb3Igb25lIGRlYXRoIGFuZCB0aGF0IHRoZSBtYXhpbXVtIG51bWJlciBvZiBkZWF0aHMgaW4gdGhpcyBkYXRhIGZvciBhIHNpbmdsZSBldmVudCBpcyBpbiB0aGUgdXBwZXIgdHdlbnRpZXMuIA0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSkgKw0KICAgIGdlb21fZnJlcXBvbHkoKQ0KYGBgDQoNClRoaXMgcmVhbGx5IHNob3dzIHRoYXQgbW9zdCBzY2hvb2wgc2hvb3RpbmcgZXZlbnRzIGx1Y2tpbHkgcmVzdWx0IGluIG5vIGRlYXRocywgYnV0IHdoYXQgYXJlIHRoZSBhY3R1YWwgcHJvcG9ydGlvbnMgb2Ygc2Nob29sIHNob290aW5ncyB0aGF0IGVuZCBpbiAwIGRlYXRocywgMSBkZWF0aCwgMiBkZWF0aHMsIGV0Yy4gDQpPbmUgd2F5IHRvIGxvb2sgYXQgdGhpcyBpcyB0byBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgZXZlbnRzIHRoYXQgcmVzdWx0ZWQgaW4gZWFjaCBudW1iZXIgb2YgZGVhdGhzLiANCldlIGNhbiBkbyB0aGlzIGJ5IGRpdmlkaW5nIHRoZSBudW1iZXIgb2YgZXZlbnRzIGJ5IHRoZSBvdmVyYWxsIHN1bSBvZiBldmVudHMgYW5kIG11bHRpcGx5aW5nIGJ5IDEwMC4gDQpUaGUgYmFzZSBgcm91bmQoKWAgZnVuY3Rpb24gY2FuIHJvdW5kIHRoaXMgdmFsdWUgdG8gdGhlIG5lYXJlc3QgMSBkZWNpbWFsIHBsYWNlIGJ5IHNwZWNpZnlpbmcgdGhhdCB3ZSB3YW50IDEgZGlnaXQgYWZ0ZXIgdGhlIGRlY2ltYWwgd2l0aCBgZGlnaXRzID0gMWAuDQoNCmBgYHtyfQ0KZGVhdGhzX3BlcmNfZXZlbnQgPC0NCiAgc2hvb3RpbmdfZGF0YSAlPiUNCiAgY291bnQoYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgcmVuYW1lKCJudW1fZXZlbnRzIj0gbikgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gcm91bmQobnVtX2V2ZW50cy9zdW0obnVtX2V2ZW50cykqMTAwLCBkaWdpdHMgPTEpKQ0KDQpkZWF0aHNfcGVyY19ldmVudA0KDQpkZWF0aHNfcGVyY19ldmVudCAlPiUNCiAgZ2dwbG90KGFlcyh4ID1gS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAsIHkgPSBwZXJjZW50KSkgKw0KICAgIGdlb21fY29sKCkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgZ3JlYXRlciB0aGFuIDYwJSBvZiB0aGUgZXZlbnRzIGhhZCBubyBkZWF0aHMuIEl0IGlzIGhvd2V2ZXIsIHRoZSBwbG90IGlzIHN0aWxsIHVuc2F0aXNmYWN0b3J5IGJlY2F1c2UgdGhlcmUgaXMgc3VjaCBhIGxvbmcgdGFpbC4NCg0KTmV4dCwgd2UgY2FuIHRyeSBjb2xsYXBzaW5nIHRoZSBldmVudHMgdGhhdCByZXN1bHRlZCBpbiA0IG9yIG1vcmUgZGVhdGhzIHRvZ2V0aGVyIGFuZCBjcmVhdGUgYSBwaWUgY2hhcnQgd2hpY2ggeW91IGFyZSBsaWtlbHkgZmFtaWxpYXIgd2l0aCBhcyB3ZWxsIGFzIGFsdGVybmF0aXZlIHBsb3QgY2FsbGVkIGEgd2FmZmxlIHBsb3QuDQoNCkZpcnN0IHRvIGNvbGxhcHNlIHRoZSBwZXJjZW50YWdlIGZvciB0aGUgZXZlbnRzIHRoYXQgaGFkIDQgb3IgbW9yZSBkZWF0aHMsIHdlIG5lZWQgdG8gZG8gYSBiaXQgb2Ygd3JhbmdsaW5nLg0KDQpXZSB3aWxsIHN0YXJ0IHdpdGggZmlsdGVyaW5nIHRoZSBkYXRhIHRvIG9ubHkgdGhlc2UgZXZlbnRzIGFuZCB0aGVuIHdlIHdpbGwgc3VtIGVhY2ggb2YgdGhlIGNvbHVtbnMgdXNpbmcgdGhlIGJhc2UgUiBmdW5jdGlvbiBgY29sU3VtcygpYCB3aXRoIHRoZSBnb2FsIG9mIGNyZWF0aW5nIGEgbmV3IHJvdyBpbiB0aGUgYGRlYXRoc19wZXJjX2V2ZW50YCBvYmplY3QgdGhhdCB3aWxsIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgYWxsIGV2ZW50cyB3aXRoIDQgb3IgbW9yZSBkZWF0aHMuIFdlIHdpbGwgdXNlIHRoZSBgPj1gIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBvcGVyYXRvci4NCg0KYGBge3J9DQpncmVhdGVyX3RoYW40IDwtIA0KICBkZWF0aHNfcGVyY19ldmVudCAlPiUgDQogIGZpbHRlcihgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAgPj0gNCkgJT4lIA0KICBjb2xTdW1zKCkNCg0KZ3JlYXRlcl90aGFuNA0KYGBgDQpHb29kLCBub3cgd2Uga25vdyB0aGUgb3ZlcmFsbCBwZXJjZW50YWdlIGZvciB0aGUgZXZlbnRzIHRoYXQgdW5mb3J0dW5hdGVseSByZXN1bHRlZCBpbiBtb3JlIHRoYW4gNCBkZWF0aHMuIA0KDQpOZXh0LCB3ZSBjb21iaW5lIHRoaXMgd2l0aCB0aGUgcmVzdCBvZiBvdXIgZGF0YSB1c2luZyB0aGUgYGJpbmRfcm93cygpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2Ugd2hpY2ggYXBwZW5kcyBhIHRpYmJsZSB0byBhbm90aGVyLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXR3aWR0aCA9ICI0MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImJpbmRyb3dzLnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKQ0KDQoNCmBgYHtyfQ0KZGVhdGhzX3BlcmNfZXZlbnQgJTw+JQ0KICBiaW5kX3Jvd3MoZ3JlYXRlcl90aGFuNCkNCg0KZGVhdGhzX3BlcmNfZXZlbnQgDQpgYGANCg0KTmV4dCwgd2UgYWRkIGEgbmV3IHZhcmlhYmxlIHNvIHRoYXQgaXQgaXMgZWFzeSB0byBwbG90IGFuZCBpbnRlcnByZXQgdGhlIG51bWJlciBvZiBkZWF0aHMgZm9yIGVhY2ggcGVyY2VudGFnZS4gDQoNCldlIHdpbGwgYWRkIHRoZSB3b3JkICJkZWF0aHMiIHRvIGVhY2ggdmFsdWUgaW4gdGhlIGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCB2YXJpYWJsZSB1c2luZyB0aGUgYmFzZSBgcGFzdGUwKClgIGZ1bmN0aW9uLiBOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBhdXRvbWF0aWNhbGx5IHdpbGwgcmVzdWx0IGluIG5vIHNwYWNlIG9yIGFueSBvdGhlciBjaGFyYWN0ZXIgYmV0d2VlbiBwYXN0ZWQgZWxlbWVudHMuIFRoZSBgcGFzdGUoKWAgZnVuY3Rpb24gY2FuIGFsdGVybmF0aXZlbHkgYmUgdXNlZCBmb3IgdGhvc2UgY2FzZXMuIA0KDQpgYGB7cn0NCmRlYXRoc19wZXJjX2V2ZW50ICU8PiUgDQogIG11dGF0ZShjYXRlZ29yeSA9IHBhc3RlMChgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIiBkZWF0aHMgIiwgIigiLCBwZXJjZW50LCAiJSkiKSkgDQoNCmRlYXRoc19wZXJjX2V2ZW50DQpgYGANCg0KV2UgY2FuIGNoYW5nZSB0aGUgdmFsdWUgZm9yIHRoZSBsYXN0IHJvdyBhYm91dCB0aGUgZXZlbnRzIHRoYXQgcmVzdWx0ZWQgaW4gbW9yZSB0aGFuIDQgZGVhdGhzLiANCg0KV2UgY2FuIHVzZSB0aGUgYGxhc3QoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlIGNvbWJpbmVkIHdpdGggdGhlIGBwdWxsKClgIGZ1bmN0aW9uIHRvIHNwZWNpZmljYWxseSBncmFiIHRoaXMgdmFsdWUuDQoNCmBgYHtyfQ0KbGFzdChwdWxsKGRlYXRoc19wZXJjX2V2ZW50LCBjYXRlZ29yeSkpDQpgYGANCg0KVXNpbmcgdGhlIGBjYXNlX3doZW4oKWAgZnVuY3Rpb24sIHdlIGNhbiBjaGFuZ2UgdGhpcyB2YWx1ZToNCg0KYGBge3J9DQpkZWF0aHNfcGVyY19ldmVudCAlPD4lIA0KICBtdXRhdGUoY2F0ZWdvcnkgPQ0KICAgICAgICAgICBjYXNlX3doZW4oY2F0ZWdvcnkgPT0gbGFzdChwdWxsKGRlYXRoc19wZXJjX2V2ZW50LCBjYXRlZ29yeSkpIH4gDQogICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjQrIGRlYXRocyAiLCAiKCIsIHBlcmNlbnQsICIlKSIpLA0KICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnkgPT0gIjEgZGVhdGhzIiB+ICIxIGRlYXRoIiwNCiAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBjYXRlZ29yeSkpDQoNCmRlYXRoc19wZXJjX2V2ZW50DQpgYGANCg0KIyMjIyB7LnRoaW5rX3F1ZXN0aW9uX2Jsb2NrfQ0KDQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KV2UgY291bGQgb2YgdXNlZCB0aGVgc3RyX3JlcGxhY2UoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHN0cmluZ3JgIHBhY2thZ2UgdG8gcmVwbGFjZSB0aGUgdmFsdWUgZm9yIHRoZSBsYXN0IHJvdy4gVGhpcyBmdW5jdGlvbiB3b3VsZCBkaXJlY3RseSBjaGFuZ2UgdGhlIHZhbHVlIG9mICI4NSBkZWF0aHMiIHRvICI0K2RlYXRocyIsIGJ1dCB0aGlzIHdvdWxkIG5vdCBiZSBhcyByZXByb2R1Y2libGUuIFdoeSBpcyB0aGF0PyANCg0KKioqDQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGFuc3dlci4gPC9zdW1tYXJ5Pg0KDQpTYXkgd2UgdXNlZCB0aGlzIGNvZGUgYWdhaW4gYWZ0ZXIgdGhlIGRhdGEgZ290IHVwZGF0ZWQuIFRoZW4gdGhlcmUgbWF5IGJlIG1vcmUgZGVhdGhzIGluIHRoaXMgY2F0ZWdvcnkgYW5kIHRoZXJlZm9yZSB0aGlzIHZhbHVlIHdvdWxkIG5vIGxvbmdlciBiZSAiODUgZGVhdGhzIi4gSW5zdGVhZCwgYnkgdXNpbmcgYGNhc2Vfd2hlbigpYCwgd2UgY2FuIHVzZSBhbiBleHByZXNzaW9uIGZvciB0aGUgbGFzdCB2YWx1ZSBvZiB0aGUgYGRlYXRoc19wZXJjX2V2ZW50YCB0aWJibGUgYW5kIHJlcGxhY2UgdGhhdCwgcmVnYXJkbGVzcyBvZiB3aGF0IHRoZSB2YWx1ZSBpcywgd2l0aCAiNCtkZWF0aHMiLiBSZWNhbGwgdGhhdCBgY2FzZV93aGVuKClgIHJlcGxhY2VzIGFsbCBvdGhlciB2YWx1ZXMgdGhhdCBhcmUgbm90IHNwZWNpZmllZCB3aXRoIGBOQWAuIFdlIGRvIG5vdCB3YW50IHRvIGxvc2UgdGhlIG90aGVyIHZhbHVlcyBmb3IgdGhlIGBjYXRlZ29yeWAgdmFyaWFibGUuIFNvIHRvIGF2b2lkIHRoaXMsIHdlIGFzc2lnbiBlYWNoIG9mIHRoZSB2YWx1ZXMgdGhhdCBhcmUgbm90IHRoZSBsYXN0IHZhbHVlIG9yIHRoZSBgIjEgZGVhdGhzImAgdmFsdWUgdG8gd2hhdCB0aGV5IGN1cnJlbnRseSBhcmUgZm9yIHRoZSBgY2F0ZWdvcnlgIHZhcmlhYmxlLCB1c2luZyBgVFJVRSB+IGNhdGVnb3J5YCAoTm90ZSB0aGF0IGFsbCByZW1haW5pbmcgdW5hc3NpZ25lZCB2YWx1ZXMgYXJlIGluZGljYXRlZCBhcyBgVFJVRWApLg0KDQpXZSBjb3VsZCBhbHNvIGFjdHVhbGx5IHR5cGUgb3V0IHRoZSBwZXJjZW50YWdlIG9mIDQrZGVhdGggY2FzZXMsIGJ1dCBpdCBpcyBhbHdheXMgbW9yZSByZXByb2R1Y2libGUgdG8gaW5zdGVhZCB1c2UgYW4gZXhwcmVzc2lvbiB0aGF0IHdpbGwgZXZhbHVhdGUgdG8gdGhlIHZhbHVlIHdlIHdhbnQuIFRoaXMgd2F5IGlmIHdlIHdlcmUgdG8gdXBkYXRlIG91ciBkYXRhIHdpdGggYWRkaXRpb25hbCBzY2hvb2wgc2hvb3RpbmcgZXZlbnRzLCB0aGlzIGV2YWx1YXRpb24gd291bGQgYWxzbyB1cGRhdGUuDQoNCjwvZGV0YWlscz4NCg0KKioqDQoNCiMjIyMNCg0KDQpPSywgdGhpcyBsb29rcyBhcyB3ZSBob3BlZC4gT0ssIG5vdyB3ZSBhcmUgcmVhZHkgdG8gbWFrZSBwbG90cy4gDQoNCkxldCdzIHN0YXJ0IHdpdGggdGhlIHBpZSBjaGFydC4gSGlzdG9yaWNhbGx5LCB0aGlzIGhhcyBiZWNvbWUgYSBiaXQgY29udHJvdmVyc2lhbCB0eXBlIG9mIHBsb3QuIEhvd2V2ZXIsIGl0IGNhbiBiZSB2ZXJ5IHVzZWZ1bCB3aGVuIHlvdSBhcmUgYWN0dWFsbHkgbG9va2luZyBhdCBwZXJjZW50YWdlcyBhbmQgdGhlIGdvYWwgaXMgdG8gc2VlIG1ham9yIHRyZW5kcyBpbiB0aGUgZGF0YSwgc3VjaCBhcyBhbGwgdGhlIGdyb3VwcyBhcmUgcm91Z2hseSBlcXVhbCBvciBvbmUgZ3JvdXAgaXMgcGFydGljdWxhcmx5IGxhcmdlciB0aGFuIHRoZSByZXN0LiBXaGVuIHRoaXMgaXMgdGhlIGNhc2UgYW5kIHlvdSBhcmUgcHJlc2VudGluZyB0aGUgZGF0YSB0byBhbiBhdWRpZW5jZSB0aGF0IGlzIGxlc3MgZmFtaWxpYXIgd2l0aCBkYXRhIHNjaWVuY2UsIHRoZXkgbWF5IGV4cGVjdCB0byBzZWUgYSBwaWUgY2hhcnQuIFRodXMgaXQgaXMgdXNlZnVsIHRvIGtub3cgaG93IHRvIG1ha2Ugb25lLiBIb3dldmVyLCBpbiBtb3N0IG90aGVyIGNhc2VzIHBpZSBjaGFydHMgZG8gYSBwb29yIGpvYiBhdCBhbGxvd2luZyB1cyB0byBzZWUgbW9yZSBzdWJ0bGUgZGlmZmVyZW5jZXMsIGFuZCB0aGV5IGFyZSBwYXJ0aWN1bGFybHkgY29uZnVzaW5nIHdoZW4gd2UgYXJlIG5vdCBsb29raW5nIGF0IHByb3BvcnRpb25zLCBidXQgcmF3IGNvdW50cy4gSW4gdGhvc2UgY2FzZXMgaXQgaXMgYmV0dGVyIHRvIHVzZSBhIGJhciBjaGFydCBhcyB3ZSBoYXZlIGFscmVhZHkgZG9uZS4gDQoNClRoZXJlIGlzIG5vIGBnZW9tXypgIGZ1bmN0aW9uIHRoYXQgYWxsb3dzIHlvdSB0byBjcmVhdGUgYSBwaWUgY2hhcnQgZGlyZWN0bHkuIEluc3RlYWQgd2Ugd2lsbCBjcmVhdGUgb3VyIGJhciBwbG90IGFzIHdlIGhhdmUgYW5kIHRoZW4gdXNlIHRoZSBgY29vcmRfcG9sYXIoKWAgZnVuY3Rpb24gdG8gd3JhcCBvdXIgeSBheGlzIGludG8gYSBjaXJjdWxhciBzaGFwZS4NCg0KYGBge3J9DQpkZWF0aHNfcGVyY19ldmVudCAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+MC41KSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50LCBmaWxsID0gY2F0ZWdvcnkpKSArDQogICAgICAjIGFkZGluZyBjb2xvciBoZXJlIGFkZHMgYSBibGFjayBvdXRsaW5lDQogICAgZ2VvbV9jb2woY29sb3IgPSAiYmxhY2siKSArDQogICAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPSBOVUxMKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArDQogICAgbGFicyh0aXRsZSA9ICJQZXJjZW50YWdlcyBvZiBzY2hvb2wgc2hvb3RpbmcgZGVhdGhzXG4oaW5jbHVkaW5nIHRoZSBzaG9vdGVyKSIpDQpgYGANCg0KVGhpcyBpcyBhY3R1YWxseSBhIGZhaXJseSBlYXN5IHBsb3QgdG8gaW50ZXJwcmV0LiBXZSBjYW4gc2VlIHRoYXQgbW9zdCBldmVudHMgcmVzdWx0ZWQgaW4gemVybyBkZWF0aHMgYW5kIHRoYXQgdGhlIG5leHQgbGFyZ2VzdCBwcm9wb3J0aW9uIHJlc3VsdGVkIGluIG9uZSBkZWF0aCwgd2hpbGUgYSBzaXphYmxlIGJ1dCBzbWFsbCBwcm9wb3J0aW9uIHJlc3VsdGVkIGluIHR3byBkZWF0aHMuIEEgdmVyeSBzbWFsbCBwcm9wb3J0aW9uIHJlc3VsdGVkIGluIHRocmVlIG9yIGZvdXIgb3IgbW9yZSBkZWF0aHMuDQoNCldlIGFsc28gY2FuIGNyZWF0ZSBhIHdhZmZsZSBwbG90LiBUaGlzIHBsb3Qgb2ZmZXJzIG9uZSBhZHZhbnRhZ2Ugb3ZlciB0aGUgcGllIGNoYXJ0LCBpbiB0aGF0IGl0IGFsc28gYWxsb3dzIGZvciBlYXNpZXIgaW50ZXJwcmV0YXRpb24gb2YgbW9yZSBzdWJ0bGUgcHJvcG9ydGlvbiBkaWZmZXJlbmNlcyB3aGlsZSBhbHNvIHNob3dpbmcgYmlnIHBpY3R1cmUgZGlmZmVyZW5jZXMgaW4gZWZmaWNpZW50IG1hbm5lci4gDQoNCkZpcnN0LCB3ZSBmaWx0ZXIgZm9yIG9ubHkgdGhlIGRhdGEgdGhhdCB3ZSB3YW50IHRvIHBsb3QuIFdlIG9ubHkgd2FudCB0aGUgMCwxLDIsMywgb3IgNCsgY2F0ZWdvcmllcy4gV2UgY2FuIGRvIHNvIGJ5IHVzaW5nIHRoZSBgc3RyX2RldGVjdCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgc3RyaW5ncmAgcGFja2FnZS4gVGhpcyBhbGxvd3MgdXMgdG8gZmluZCB0aGUgdmFsdWVzIHRoYXQgbWF0Y2ggbXVsdGlwbGUgcGF0dGVybnMuIFRoZSBwYXR0ZXJucyBhcmUgc2VwYXJhdGVkIGJ5IHRoZSBgfGAgb3Igb3BlcmF0b3IuIFRodXMgYW55IHZhbHVlIG1hdGNoaW5nIGFueSBvZiB0aGUgcGF0dGVybnMgc2hvdWxkIGJlIGtlcHQuIE5vdGljZSB0aGF0IHRoZSBgXFxgIGlzIG5lY2Vzc2FyeSBiZWZvcmUgdGhlIGArYCBzbyB0aGF0IGlzIG5vdCBpbnRlcnByZXRlZCBhcyBhIG1hdGhlbWF0aWNhbCBwbHVzIHNpZ24uIA0KDQpUaGUgYHdhZmZsZSgpYCBmdW5jdGlvbiByZXF1aXJlcyB0aGF0IHRoZSBkYXRhIGJlIGluIHdpZGUgZm9ybWF0LiBUaHVzIHdlIG5lZWQgdG8gdXNlIGBwaXZvdF93aWRlcigpYCBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIHRvIGRvIHNvLiBUaGlzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgYHBpdm90X2xvbmdlcigpYCBmdW5jdGlvbiwgaG93ZXZlciBpbiB0aGlzIGNhc2Ugd2UgbmVlZCB0byBzcGVjaWZ5IHdoYXQgZXhpc3RpbmcgY29sdW1uIGNvbnRhaW5zIHRoZSBuYW1lcyBmb3IgdGhlIG5ldyBjb2x1bW5zIHVzaW5nIGBuYW1lc19mcm9tYCBhbmQgd2hhdCBleGlzdGluZyBjb2x1bW4gY29udGFpbnMgdGhlIHZhbHVlcyBmb3IgdGhlIG5ldyBjb2x1bW5zIHVzaW5nIGB2YWx1ZXNfZnJvbWAuIA0KDQpgYGB7cn0NCmRlYXRoc19wZXJjX2V2ZW50ICU+JSANCiAgc2VsZWN0KC1gS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApICU+JQ0KICBmaWx0ZXIoc3RyX2RldGVjdChjYXRlZ29yeSwgIjAgZGVhdGhzfDEgZGVhdGh8MiBkZWF0aHN8MyBkZWF0aHN8NFxcKyIpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZChwZXJjZW50KSkgJT4lDQogIHNlbGVjdCgtbnVtX2V2ZW50cykgJT4lDQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY2F0ZWdvcnksIA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwZXJjZW50KSAlPiUNCiAgd2FmZmxlOjp3YWZmbGUobGVnZW5kX3BvcyA9ICJib3R0b20iLCB0aXRsZT0iRGVhdGhzIFBlciBTY2hvb2wgU2hvb3RpbmciLCANCiAgICAgICAgICAgICAgICAgeGxhYj0iMSBzcXVhcmUgfiAxJSIpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQ0KYGBgDQoNCiMjIyBQZXJjZW50YWdlcw0KDQpXZSBhcmUgYWxzbyBpbnRlcmVzdGVkIGluIGluY2x1ZGluZyBzdGF0aXN0aWNzIGluIG91ciBkYXNoYm9hcmQuIEZvciBleGFtcGxlLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBob3cgbWFueSBzaG9vdGVycyBjb21taXR0ZWQgb3IgYXR0ZW1wdGVkIHN1aWNpZGUuDQoNCldlIHByZXZpb3VzbHkgY29udmVydGVkIHZhcmlhYmxlcyB3aXRoIGB5ZXNgIG9yIGBub2AgYW5zd2VycyBiZWNhdXNlIHRoZXkgd2VyZSBpbmNvbnNpc3RlbnRseSBjb2RlZCBhcyBgeWVzYC8gYHlgIGFuZCBgbm9gL2BuYC4gIEZ1cnRoZXJtb3JlLCBsb2dpY2FsIHZhcmlhYmxlcyBhcmUgZWFzaWVyIHRvIHdvcmsgd2l0aCBpbiB0ZXJtcyBvZiBwZXJmb3JtaW5nIGNhbGN1bGF0aW9ucyBiZWNhdXNlIGBUUlVFYCB2YWx1ZXMgYXJlIHRyZWF0ZWQgbGlrZSBhIGAxYCB3aGlsZSBgRkFMU0VgIHZhbHVlcyBhcmUgdHJlYXRlZCBsaWtlIGEgYDBgLiANCg0KV2UgY2FuIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBzaG9vdGVycyB0aGF0IGNvbW1pdHRlZCBvciBhdHRlbXB0ZWQgc3VpY2lkZSBvdXQgb2YgYWxsIGVudHJpZXMgdGhhdCBoYXZlIGRhdGEgZm9yIHRoaXMgaW5mb3JtYXRpb24uIFRodXMgd2UgZG8gbm90IHdhbnQgdG8gaW5jbHVkZSBgTkFgIHZhbHVlcyBpbiB0aGUgY2FsY3VsYXRpb24sIG90aGVyd2lzZSB0aGlzIG1pZ2h0IGdpdmUgdXMgYSBkaXN0b3J0ZWQgcGljdHVyZSBvZiB0aGUgdHJ1dGguDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkYXRhIGZvciB0aGlzIHZhcmlhYmxlOg0KDQpgYGB7cn0NCnNob290aW5nX2RhdGEgJT4lIA0KICBjb3VudChgU3VpY2lkZSAob3IgYXR0ZW1wdGVkIHN1aWNpZGUpIGJ5IFNob290ZXIgKFkvTilgKQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgNDUgYE5BYCB2YWx1ZXMuIA0KDQpJZiB3ZSBjYWxjdWxhdGUgYSBzdW0gb2YgdGhlIGBUUlVFYCB2YWx1ZXMsICh3aGljaCBhcmUgdGhvc2UgdGhhdCBhcmUgZXF1aXZhbGVudCB0byBgMWApLCB3ZSBjYW4gZG8gc28gYnkganVzdCBzdW1taW5nIHRoaXMgdmFyaWFibGUsIHdoaWNoIGlzIGVxdWl2YWxlbnQgdG8gc3VtbWluZyB2YWx1ZXMgdGhhdCBhcmUgZ3JlYXRlciB0aGFuIGAwYC4gDQoNCmBgYHtyfQ0Kc3VtKHB1bGwoc2hvb3RpbmdfZGF0YSwgDQogICAgICAgICBgU3VpY2lkZSAob3IgYXR0ZW1wdGVkIHN1aWNpZGUpIGJ5IFNob290ZXIgKFkvTilgKSwgDQogICAgbmEucm0gPSBUUlVFKQ0Kc3VtKHB1bGwoc2hvb3RpbmdfZGF0YSwgDQogICAgICAgICBgU3VpY2lkZSAob3IgYXR0ZW1wdGVkIHN1aWNpZGUpIGJ5IFNob290ZXIgKFkvTilgKSA+IDAsIA0KICAgIG5hLnJtID0gVFJVRSkNCmBgYA0KDQpJbiBjb250cmFzdCwgYEZBTFNFYCB2YWx1ZXMgYXJlIHRob3NlIHRoYXQgYXJlIGVxdWl2YWxlbnQgdG8gYDBgLiBUaHVzIGlmIHdlIHdhbnQgdG8gZGl2aWRlIGJ5IHRoZSBzdW0gb2YgYWxsIHZhbHVlcyB0aGF0IGFyZSBgRkFMU0VgIGFyZSBgVFJVRWAsIHRoZW4gd2UgY2FuIHN1bSBhbGwgdmFsdWVzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBgMGAuDQoNCmBgYHtyfQ0Kc3VtKHB1bGwoc2hvb3RpbmdfZGF0YSwgDQogICAgICAgICBgU3VpY2lkZSAob3IgYXR0ZW1wdGVkIHN1aWNpZGUpIGJ5IFNob290ZXIgKFkvTilgKSA+PSAwLCANCiAgICBuYS5ybSA9IFRSVUUpDQpgYGANCg0KVGh1cywgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBhbGwgcmVwb3J0aW5nIHZhbHVlcyBsaWtlIHNvLCB3aGVyZSB0aGUgYFRSVUVgIHZhbHVlcyBhcmUgZGl2aWRlZCBieSB0aGUgc3VtIG9mIGFsbCBgVFJVRWAgYW5kIGBGQUxTRWAgdmFsdWVzOiAoV2UgYWxzbyBtdWx0aXBseSBieSAxMDAgdXNpbmcgYCoxMDBgIHRvIGdldCB0aGUgcGVyY2VudGFnZSB2YWx1ZS4pIA0KDQpgYGB7cn0NCnN1aWNpZGUgPC0gDQogIChzdW0ocHVsbChzaG9vdGluZ19kYXRhLGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApLCBuYS5ybSA9IFRSVUUpIC8NCiAgIHN1bShwdWxsKHNob290aW5nX2RhdGEsIGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApPj0wLCBuYS5ybSA9IFRSVUUpKSoxMDANCg0Kc3VpY2lkZQ0KYGBgDQoNCldlIGNhbiB1c2UgdGhlIGByb3VuZCgpYCBmdW5jdGlvbiB0byByb3VuZCB0aGlzIHZhbHVlIGFuZCB0aGUgYGZvcm1hdCgpYCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgdmFsdWUgaGFzIHRoZSBjb3JyZWN0IG51bWJlciBvZiBkaWdpdHMuIA0KDQpgYGB7cn0NCnN1aWNpZGUgPC0gcm91bmQoc3VpY2lkZSwgMikNCnN1aWNpZGUNCmBgYA0KDQpJZiBhZnRlciByb3VuZGluZyB3ZSB3YW50ZWQgemVyb3MgYWZ0ZXIgdGhlIGRlY2ltYWwgc28gdGhhdCB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBhZnRlciB0aGUgZGVjaW1hbCB3YXMgY29uc2lzdGVudCBmb3IgdGhlIGRpZmZlcmVudCBzdGF0aXN0aWNzIHdlIHdlcmUgZGlzcGxheWluZywgd2UgY291bGQgdXNlIHRoZSBgZm9ybWF0KClgIGZ1bmN0aW9uIHRvIHNwZWNpZnkgdGhpcy4gDQoNClNvIHdlIGNhbiBhZGQgYSB6ZXJvIGFmdGVyICBgMTMuNGAgbGlrZSBzbzoNCg0KYGBge3J9DQpmb3JtYXQoc3VpY2lkZSwgbnNtYWxsID0gMikNCmBgYA0KDQpUbyBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2Ygc2Nob29sIHNob290aW5ncyB3aGVyZSB0aGlzIGluZm9ybWF0aW9uIHdhcyByZXBvcnRlZCB3ZSBjYW4gZG8gdGhlIGZvbGxvd2luZywgYnkgY2FsY3VsYXRpbmcgYWxsIHZhbHVlcyB0aGF0IGFyZSBub3QgYE5BYCB1c2luZyBgPj0wYCBhbmQgY2FsY3VsYXRpbmcgdGhlIG51bWJlciBhbGwgcG9zc2libGUgdmFsdWVzIHVzaW5nIHRoZSBiYXNlIGBsZW5ndGgoKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KcmVwb3J0aW5nX3N1aWMgPC0gDQogIChzdW0ocHVsbChzaG9vdGluZ19kYXRhLCANCiAgICAgICAgICAgIGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApPj0wLCANCiAgICAgICBuYS5ybSA9IFRSVUUpIC8NCiAgIGxlbmd0aChwdWxsKHNob290aW5nX2RhdGEsIA0KICAgICAgICAgICAgICAgYFN1aWNpZGUgKG9yIGF0dGVtcHRlZCBzdWljaWRlKSBieSBTaG9vdGVyIChZL04pYCkpDQogICApKjEwMA0KDQpyZXBvcnRpbmdfc3VpYyA8LSByb3VuZChyZXBvcnRpbmdfc3VpYywgMSkNCnJlcG9ydGluZ19zdWljDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IDk3JSBvZiB0aGUgc2Nob29sIHNob290aW5ncyBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoaXMgdmFyaWFibGUuDQoNCkl0IGlzIGltcG9ydGFudCB0byBjaGVjayBhbmQgcmVwb3J0IHRoaXMgcGVyY2VudGFnZSBzbyB0aGF0IHBlb3BsZSBjYW4gYmV0dGVyIHVuZGVyc3RhbmQgaWYgb3VyIHBlcmNlbnRhZ2VzIGFyZSByZWxpYWJsZS4gSWYgb25seSAyJSBvZiB0aGUgc2Nob29sIHNob290aW5ncyBoYWQgdGhpcyBpbmZvcm1hdGlvbiBhbmQgaW4gYWxsIGNhc2VzIG9mIHRoZSAyJSB0aGUgc2Nob29sIHNob290aW5ncyBpbnZvbHZlZCBhIHN1aWNpZGUgKG9yIGF0dGVtcHQpLCB0aGVuIHRoaXMgd291bGQgbGVhZCBwZW9wbGUgdG8gYmVsaWV2ZSB0aGF0IDEwMCUgb2Ygc2Nob29sIHNob290aW5ncyBpbnZvbHZlIGEgc2hvb3RlciBzdWljaWRlIChvciBhdHRlbXB0KS4gVGhpcyB3b3VsZCBjbGVhcmx5IGJlIG1pc2xlYWRpbmchICBJbiBvdXIgY2FzZSB0aGUgbWFqb3JpdHkgb2YgdGhlIHNjaG9vbCBzaG9vdGluZ3MgaW5jbHVkZWQgdGhpcyBpbmZvcm1hdGlvbiwgc28gd2Ugd2lsbCBpbmRlZWQgcmVwb3J0IHRoZSBwZXJjZW50YWdlIGFuZCB3ZSB3aWxsIGFsc28gbGV0IHBlb3BsZSBrbm93IGhvdyBtdWNoIG9mIHRoZSBzY2hvb2wgc2hvb3RpbmcgZGF0YSBoYWQgdGhpcyBpbmZvcm1hdGlvbi4NCg0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KTm93IHRyeSB0byBwZXJmb3JtIHZhcmlhdGlvbnMgb2YgdGhlc2UgY2FsY3VsYXRpb25zIHRvIGNhbGN1bGF0ZSBvdGhlciBzdGF0aXN0aWNzIGZvciBvdXIgZGFzaGJvYXJkLCBzdWNoIGFzIHRoZSBwZXJjZW50YWdlIG9mIHRoZSBzaG9vdGVycyB0aGF0IHdlcmUgbWFsZSBvciB0aGUgcGVyY2VudGFnZSBvZiBldmVudHMgd2hlcmUgYSBzaW5nbGUgaGFuZGd1biB3YXMgdXNlZCwgKGhpbnQgdGhlIGBGaXJlYXJtIFR5cGVgIHZhbHVlIHdpbGwgYmUgYEhhbmRndW5gKS4NCg0KKioqDQoNCioqU2hvb3RlciBXYXMgTWFsZSoqDQoNCioqKg0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+DQoNCmBgYHtyfQ0KDQoNCmdlbmRlciA8LSBwYXN0ZShhcy5jaGFyYWN0ZXIocm91bmQoMTAwICogKHN1bSgNCiAgICBjYXNlX3doZW4ocHVsbChzaG9vdGluZ19kYXRhLGBTaG9vdGVyIEdlbmRlcmApID09ICJNYWxlIiB+IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkNCiAgICAvDQogICAgICBzdW0ocHVsbChzaG9vdGluZ19kYXRhLCBgU2hvb3RlciBHZW5kZXJgKT49MCwgbmEucm0gPSBUUlVFKSksDQogICAgMSkpLCAiJSIpDQoNCnJlcG9ydGluZ19tYWxlIDwtIChzdW0ocHVsbChzaG9vdGluZ19kYXRhLCBgU2hvb3RlciBHZW5kZXJgKT49MCwgbmEucm0gPSBUUlVFKS8NCiAgICAgICAgICAgICAgbGVuZ3RoKHB1bGwoc2hvb3RpbmdfZGF0YSwgYFNob290ZXIgR2VuZGVyYCkpKSoxMDANCnJlcG9ydGluZ19tYWxlIDwtIHJvdW5kKHJlcG9ydGluZ19tYWxlLCAxKQ0KDQoNCmdlbmRlcg0KcmVwb3J0aW5nX21hbGUNCg0KYGBgDQoNCjwvZGV0YWlscz4NCg0KKioqDQoNCioqVXNlIG9mIGEgU2luZ2xlIEhhbmRndW4qKg0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgY29kZS4gPC9zdW1tYXJ5Pg0KDQoNCmBgYHtyfQ0KaGFuZGd1biA8LXBhc3RlKGFzLmNoYXJhY3Rlcihyb3VuZCgxMDAgKihzdW0oY2FzZV93aGVuKA0KICAgICAgcHVsbChzaG9vdGluZ19kYXRhLGBGaXJlYXJtIFR5cGVgKSA9PSAiSGFuZGd1biIgfiBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBGQUxTRSksIG5hLnJtID0gVFJVRSkNCiAgICAvDQogICAgICBzdW0ocHVsbChzaG9vdGluZ19kYXRhLCBgRmlyZWFybSBUeXBlYCk+PTAsIG5hLnJtID0gVFJVRSkpLA0KICAgIDEpKSwgIiUiKQ0KDQpyZXBvcnRpbmdfZ3VuIDwtIChzdW0ocHVsbChzaG9vdGluZ19kYXRhLCBgRmlyZWFybSBUeXBlYCk+PTAsIG5hLnJtID0gVFJVRSkvDQogICAgICAgICAgICAgIGxlbmd0aChwdWxsKHNob290aW5nX2RhdGEsIGBGaXJlYXJtIFR5cGVgKSkpKjEwMA0KcmVwb3J0aW5nX2d1biA8LSByb3VuZChyZXBvcnRpbmdfZ3VuLCAxKQ0KDQpoYW5kZ3VuDQpyZXBvcnRpbmdfZ3VuDQpgYGANCjwvZGV0YWlscz4NCg0KKioqDQoNCiMjIyMNCg0KDQojICoqRGFzaGJvYXJkIEJhc2ljcyoqDQoqKioNCg0KV2UgYXJlIG5vdyByZWFkeSB0byBidWlsZCBvdXIgZGFzaGJvYXJkIQ0KDQpMZXQncyBpbnRyb2R1Y2Ugc29tZSBiYXNpY3MgYWJvdXQgY3JlYXRpbmcgZGFzaGJvYXJkcyBpbiBSIGluIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4NCg0KTm90ZSB0aGF0IHlvdSBjYW4gYWxzbyBzdGFydCB0aGUgY2FzZSBzdHVkeSBhdCB0aGlzIHBvaW50LCB3ZSB3aWxsIGxldCB5b3Uga25vdyBob3cgdG8gZ2V0IHRoZSBkYXRhIHRoYXQgeW91IG5lZWQuDQoNCiMjICoqRGFzaGJvYXJkIHBhY2thZ2VzKioNCioqKg0KDQpUbyBtYWtlIG91ciBkYXNoYm9hcmQgd2Ugd2lsbCB1c2UgdGhyZWUgdmVyeSB1c2VmdWwgcGFja2FnZXM6DQoNCjEuIFtmbGV4ZGFzaGJvYXJkXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkLykNCg0KRmxleGRhc2hib2FyZCBpcyBhIHBhY2thZ2UgdGhhdCB3YXMgY3JlYXRlZCBieSBSU3R1ZGlvIGFuZCBbcmVsZWFzZWRdKGh0dHBzOi8vYmxvZy5yc3R1ZGlvLmNvbS8yMDE2LzA1LzE3L2ZsZXhkYXNoYm9hcmQtZWFzeS1pbnRlcmFjdGl2ZS1kYXNoYm9hcmRzLWZvci1yLykgaW4gTWF5IG9mIDIwMTYuIFRoaXMgcGFja2FnZSBhbGxvd3MgZm9yIHVzZXJzIHRvIG1vcmUgZWFzaWx5IGNyZWF0ZSBkYXNoYm9hcmRzIHVzaW5nIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tLykuIA0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL2ludHJvZHVjaW5nLWZsZXhkYXNoYm9hcmRzLykgZm9yIGEgdmlkZW8gYWJvdXQgZmxleGRhc2hib2FyZCBhbmQgW2hlcmVdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvKSBmb3IgYSBtb3JlIGluZm9ybWF0aW9uIG9uIGhvdyB0byB1c2UgdGhpcyBwYWNrYWdlLg0KDQoyLiBbbGVhZmxldF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0LykNCg0KW0xlYWZsZXRdKGh0dHBzOi8vbGVhZmxldGpzLmNvbS8pIGlzIHRoZSBsZWFkaW5nIG9wZW4tc291cmNlIEphdmFTY3JpcHQgbGlicmFyeSBmb3IgaW50ZXJhY3RpdmUgbWFwcyBhbmQgaXMgdXNlZCBieSBtYW55IHdlYnNpdGVzLiBUaGUgW2xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIFIgcGFja2FnZSBhbGxvd3MgZm9yIHVzZXJzIHRvIG1vcmUgZWFzaWx5IGludGVncmF0ZSBsZWFmbGV0IG1hcHMgaW4gUiwgdG8gY3JlYXRlIG1hcHMgbGlrZSB0aGUgb25lIGJlbG93LiBXZSB3aWxsIHVzZSB0aGlzIHBhY2thZ2UgdG8gY3JlYXRlIGEgbWFwIG9mIHdoZXJlIHNjaG9vbCBzaG9vdGluZ3MgaGF2ZSBvY2N1cnJlZCBpbiB0aGUgVVMuDQoNCkhlcmUgaXMgYW4gZXhhbXBsZSBvZiBhbiBpbnRlcmFjdGl2ZSBtYXAgbWFkZSB3aXRoIGBsZWFmbGV0YA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmxpYnJhcnkobWFwcykNCm1hcFN0YXRlcyA9IG1hcCgic3RhdGUiLCBmaWxsID0gVFJVRSwgcGxvdCA9IEZBTFNFKQ0KbGVhZmxldChkYXRhID0gbWFwU3RhdGVzKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkUG9seWdvbnMoZmlsbENvbG9yID0gdG9wby5jb2xvcnMoMTAsIGFscGhhID0gTlVMTCksIHN0cm9rZSA9IEZBTFNFKQ0KYGBgDQoNCjMuIFtzaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pDQoNCltTaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pIGlzIGFuIFIgcGFja2FnZSB0aGF0IG1ha2VzIGl0IGVhc2llciB0byBjcmVhdGUgaW50ZXJhY3RpdmUgd2ViIGFwcGxpY2F0aW9ucyBpbiBSLiBTZWUgW2hlcmVdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vZ2FsbGVyeS8pIGZvciBhIGdhbGxlcnkgb2YgZXhhbXBsZXMuIFBlb3BsZSBoYXZlIGNyZWF0ZWQgYSB2YXJpZXR5IG9mIGRpdmVyc2UgYXBwbGljYXRpb25zIHVzaW5nIHRoaXMgcGFja2FnZS0gZnJvbSBbaW50ZXJhY3RpdmUgd2Vic2l0ZXNdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vZ2FsbGVyeS9yZWFsLWVzdGF0ZS1pbnZlc3RtZW50Lmh0bWwpIHRvIFtnYW1lc10oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5L2hleC1tZW1vcnkuaHRtbCkuDQoNCkhlcmUgaXMgYW4gc2NyZWVuc2hvdCBvZiBhIGBzaGlueWAgYXBwLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9ICI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImdhbWUucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tL2dhbGxlcnkvaGV4LW1lbW9yeS5odG1sKQ0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvdXNpbmcuaHRtbCNjb21wb25lbnRzKSBmb3IgYSBsaXN0IG9mIG90aGVyIHBhY2thZ2VzIHRoYXQgYXJlIHVzZWZ1bCBmb3IgYWRkaW5nIGVsZW1lbnRzIHRvIGRhc2hib2FyZHMgY3JlYXRlZCB3aXRoIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4NCg0KKioqDQoNCiMjICoqUiBNYXJrZG93bioqDQoqKioNCg0KVGhlIGNhc2Ugc3R1ZHkgdGhhdCB5b3UgYXJlIHJlYWRpbmcgcmlnaHQgbm93IHdhcyBjcmVhdGVkIHVzaW5nIGFuIFtSIE1hcmtkb3duIGRvY3VtZW50XShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pLiBUaGlzIG1lYW5zIHRoYXQgaXQgaXMgYSBkb2N1bWVudCB0aGF0IHVzZXMgdGhlIGBNYXJrZG93bmAgbGFuZ3VhZ2Ugc3ludGF4IHdpdGggZW5oYW5jZWQgY2FwYWJpbGl0aWVzIG9mIGV4ZWN1dGluZyBSIGNvZGUgaW4gdGhlIGRvY3VtZW50LiANCg0KSW4gZmFjdCwgaWYgeW91IGNsaWNrIHRoZSBidXR0b24gdGhhdCBzYXlzICJjb2RlIiBvbiB0aGUgdXBwZXIgcmlnaHQgY29ybmVyIGF0IHRoZSB0b3Agb2YgdGhlIEhUTUwgeW91IHdpbGwgZG93bmxvYWQgdGhlIFtSIE1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9hcnRpY2xlc19pbnRyby5odG1sIzp+OnRleHQ9UiUyME1hcmtkb3duJTIwaXMlMjBhJTIwZmlsZSxjb2RlJTJDJTIwbGlrZSUyMHRoZSUyMGRvY3VtZW50JTIwYmVsb3cuKSBkb2N1bWVudCBmb3IgdGhpcyBjYXNlIHN0dWR5LiANCg0KW1IgTWFya2Rvd24gKFJtZCldKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2FydGljbGVzX2ludHJvLmh0bWwjOn46dGV4dD1SJTIwTWFya2Rvd24lMjBpcyUyMGElMjBmaWxlLGNvZGUlMkMlMjBsaWtlJTIwdGhlJTIwZG9jdW1lbnQlMjBiZWxvdy4pIGlzIGEgZmlsZSBmb3JtYXQgdGhhdCBjb250YWlucyBNYXJrZG93biBzeW50YXggYW5kIGVtYmVkZGVkIFIgY29kZSAoaXQgY2FuIGFsc28gaW5jb3Jwb3JhdGUgY29kZSBmcm9tIHNvbWUgb3RoZXIgbGFuZ3VhZ2VzIGxpa2UgW1B5dGhvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUHl0aG9uXyhwcm9ncmFtbWluZ19sYW5ndWFnZSkpIGFuZCBbU1FMXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TUUwpKS4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmxpYnJhcnkodmVtYmVkcikNCmVtYmVkX3VybCgiaHR0cHM6Ly92aW1lby5jb20vMTc4NDg1NDE2IikgJT4lDQogIGRpdihjbGFzcyA9ICJ2ZW1iZWRyIikgJT4lDQogIGRpdihhbGlnbiA9ICJjZW50ZXIiKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCkgDQoNCiMjIyMgey5jbGlja190b19leHBhbmRfYmxvY2t9DQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgaG93IHRoaXMgdmlkZW8gd2FzIGVtYmVkZGVkIGluIHRoaXMgUiBNYXJrZG93bi4gPC9zdW1tYXJ5Pg0KDQpUaGlzIHZpZGVvIHdhcyBpbmNsdWRlZCB1c2luZyB0aGUgYHZlbWJlZHJgIHBhY2thZ2UuIFZpZGVvcyBvbiBbVmltZW9dKGh0dHBzOi8vdmltZW8uY29tLykgb3IgW1lvdVR1YmVdKGh0dHBzOi8veW91dHViZS5jb20vKSBjYW4gYmUgYWRkZWQgbGlrZSBzbywgd2hlcmUgYSB1cmwgaXMgYWRkZWQgd2l0aGluIHF1b3RhdGlvbiBtYXJrcyBhbmQgdGhlIGZvbGxvd2luZyB0d28gbGluZXMgb2YgY29kZSBhbGxvdyBmb3IgdGhlIHZpZGVvIHRvIGJlIGNlbnRlcmVkIGluIHRoZSBSIE1hcmtkb3duIG91dHB1dC4gU2VlIFtoZXJlXShbdmVtYmVkcl0oaHR0cHM6Ly9naXRodWIuY29tL2lqbHl0dGxlL3ZlbWJlZHIpICB0byBsZWFybiBtb3JlIGFib3V0IGVtYmVkZGluZyB2aWRlb3Mgd2l0aCB0aGlzIHBhY2thZ2UuDQoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQpsaWJyYXJ5KHZlbWJlZHIpDQplbWJlZF91cmwoImh0dHBzOi8vdmltZW8uY29tLzE3ODQ4NTQxNiIpICU+JQ0KICBkaXYoY2xhc3MgPSAidmVtYmVkciIpICU+JQ0KICBkaXYoYWxpZ24gPSAiY2VudGVyIikNCmBgYA0KDQo8L2RldGFpbHM+DQoNCiMjIyMNCg0KVGhlc2UgUm1kIGZpbGVzIGNhbiBiZSByZW5kZXJlZCBpbnRvIGEgdmFyaWV0eSBvZiBmaWxlIG91dHB1dHMgbGlrZSBQREYsIHdvcmQsIEhUTUwgZXRjLiBieSB0aGUgIFtga25pdHJgXShodHRwczovL3lpaHVpLm9yZy9rbml0ci8pIGFuZCBbYHJtYXJrZG93bmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9ybWFya2Rvd24vcm1hcmtkb3duLnBkZikgcGFja2FnZXMuDQoNClRoaXMgcmVsaWVzIG9uIGNvbnZlcnNpb24gb2YgdGhlIFJtZCBmaWxlIGludG8gdGhlIFtNYXJrZG93bl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFya2Rvd24pIGxhbmd1YWdlIGJ5IHNvZnR3YXJlIGNhbGxlZCBbUGFuZG9jXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QYW5kb2MpLg0KDQpbTWFya2Rvd25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01hcmtkb3duKSAod2hpY2ggaGFzIGJlZW4gaW1wbGVtZW50ZWQgYnkgbWFueSBsYW5ndWFnZXMsIHN1Y2ggYXMgW1BlcmxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1BlcmwpLCBbSmF2YV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSmF2YV8ocHJvZ3JhbW1pbmdfbGFuZ3VhZ2UpKSwgW1B5dGhvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUHl0aG9uXyhwcm9ncmFtbWluZ19sYW5ndWFnZSkpLCBbQyNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NfU2hhcnBfKHByb2dyYW1taW5nX2xhbmd1YWdlKSksIFtSdWJ5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SdWJ5Xyhwcm9ncmFtbWluZ19sYW5ndWFnZSkpLCBldGMuKSBpcyBhIGxhbmd1YWdlIG9mIGEgcGFydGljdWxhciBjbGFzcyBvZiBwcm9ncmFtbWluZyBsYW5ndWFnZXMgY2FsbGVkIFtsaWdodHdlaWdodCBtYXJrdXAgbGFuZ3VhZ2VzIChMTUwpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaWdodHdlaWdodF9tYXJrdXBfbGFuZ3VhZ2UpLiANCg0KTE1McyBoYXZlIHJlbGF0aXZlbHkgc2ltcGxlIGFuZCBpbnR1aXRpdmUgc3ludGF4LCBhbmQgYXJlIHRoZXJlZm9yZSByZWxhdGl2ZWx5IGVhc3kgdG8gd3JpdGUgYW5kIHJlYWQgYW5kIGFyZSBjb252ZXJ0ZWQgYnkgc29mdHdhcmUgaW50byBzb21lIHR5cGUgb2YgbGVzcyBodW1hbi1mcmllbmRseSBsYW5ndWFnZSB0byBjcmVhdGUgYW4gb3V0cHV0IGRvY3VtZW50IGxpa2UgYSBQREYgb3IgYW4gSFRNTCBmaWxlLiBJbiBmYWN0LCBtdWx0aXBsZSBvdXRwdXQgZmlsZXMgY2FuIGJlIGNyZWF0ZWQgZnJvbSB0aGUgc2FtZSBMTUwgZmlsZSENCg0KSW4gb3VyIGNhc2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gcmVuZGVyaW5nIG91ciBSbWQgZG9jdW1lbnQgaW50byBhIHdlYnNpdGUuIFRoZSBjb2RlIGluIG91ciBSIE1hcmtkb3duIGRvY3VtZW50IHdpbGwgYmUgaW50ZXJwcmV0ZWQgYW5kIGNvbnZlcnRlZCB1bHRpbWF0ZWx5IGludG8gSFRNTCBjb2RlLg0KDQpBbHRob3VnaCBMTUxzIHRlbmQgdG8gYmUgcXVpdGUgc2ltaWxhciwgaGVyZSB5b3UgY2FuIHNlZSBzb21lIG9mIHRoZSBkaWZmZXJlbmNlcyBpbiBzeW50YXg6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAic3ludGF4LnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlnaHR3ZWlnaHRfbWFya3VwX2xhbmd1YWdlKQ0KDQpTZWUgdGhpcyBbYm9va10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gd29ya2luZyB3aXRoIFIgTWFya2Rvd24gZmlsZXMuIA0KDQpUaGUgUlN0dWRpbyBbY2hlYXRzaGVldCBmb3IgUiBNYXJrZG93bl0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9ybWFya2Rvd24tMi4wLnBkZikgYW5kIHRoaXMgW3R1dG9yaWFsXShodHRwczovL291cmNvZGluZ2NsdWIuZ2l0aHViLmlvL3R1dG9yaWFscy9ybWFya2Rvd24vKSBhcmUgZ3JlYXQgZm9yIGdldHRpbmcgc3RhcnRlZC4gDQoNCiMjICoqRmxleGRhc2hib2FyZCoqDQoqKioNCg0KVGhlcmUgYXJlIHNldmVyYWwgaW1wb3J0YW50IGZlYXR1cmVzIGFib3V0IHRoZSBSIE1hcmtkb3duIGxhbmd1YWdlIHRoYXQgdGhlIGBmbGV4ZGFzaGJvYXJkYCBwYWNrYWdlIGxldmVyYWdlcy4gDQoNClRoZXNlIGZlYXR1cmVzIGFyZSB1c2VkIHRvIHNwZWNpZnkgdGhlIGxheW91dCBhbmQgZWxlbWVudHMgb2YgdGhlIGRhc2hib2FyZC4NCg0KSGVyZSBhcmUgc29tZSBtYWpvciBSIE1hcmtkb3duIGZlYXR1cmVzIHRvIGtlZXAgaW4gbWluZCBmb3IgYGZsZXhkYXNoYm9hcmRgOg0KDQoxLiBUaGUgYmVnaW5uaW5nIG9mIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQgaXMgd2hhdCBpcyBjYWxsZWQgdGhlIFtZQU1MXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9ZQU1MKSBoZWFkZXIuIFRoaXMgaXMgZGVsaW5lYXRlZCBieSBgLS0tYCB0aHJlZSBkYXNoIG1hcmtzIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGhlYWRlciBZQU1MIGNvZGUuDQoNCkxpa2Ugc286DQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiMzAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJ5YW1sLnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvcm1hcmtkb3duLykNCg0KW1lBTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1lBTUwpIGlzIHlldCBhbm90aGVyIGxhbmd1YWdlLCBidXQgdW5saWtlIE1hcmtkb3duIGl0IGlzIGEgZGF0YS1vcmllbnRlZCBsYW5ndWFnZSBhbmQgaXMgb2Z0ZW4gdXNlZCBmb3IgdGhlIFtjb25maWd1cmF0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db25maWd1cmF0aW9uX2ZpbGUpIG9mIHNvZnR3YXJlIG9yIHRvIHNldCB1cCBob3cgYSBzb2Z0d2FyZSBwcm9ncmFtIHNob3VsZCB3b3JrLg0KDQpXaGF0ZXZlciBjb2RlIHlvdSBwdXQgaW4gdGhlIFlBTUwgaGVhZGVyIHdpbGwgaW5mbHVlbmNlIHRoZSByZXN0IG9mIHRoZSBkb2N1bWVudCBhbmQgZXNzZW50aWFsbHkgc2V0IHVwIGhvdyB0aGUgUiBNYXJrZG93biBkb2N1bWVudCB3aWxsIHJlbmRlci4gSW4gdGhlIGV4YW1wbGUgYWJvdmUsIHRoZSB0eXBlIG9mIG91dHB1dCBpcyBzcGVjaWZpZWQuDQoNCk90aGVyIG1vcmUgY29tcGxpY2F0ZWQgZmVhdHVyZXMgY2FuIGJlIGluY2x1ZGVkLiBGb3IgZXhhbXBsZSwgd2UgY2FuIHNwZWNpZnkgdGhhdCB3ZSBhcmUgY3JlYXRpbmcgYSBkYXNoYm9hcmQgd2l0aCBgZmxleGRhc2hib2FyZGAgYW5kIHdlIGNhbiBzcGVjaWZ5IGhvdyB3ZSB3YW50IHRoZSBsYXlvdXQgb2Ygb3VyIGRhc2hib2FyZCB0byBiZSBkaXNwbGF5ZWQgbGlrZSBzbzoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjMwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAieWFtbF9kYXNoYm9hcmQucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL2xheW91dHMuaHRtbCkNCg0KV2Ugd2lsbCBkZXNjcmliZSB0aGlzIGluIG1vcmUgZGV0YWlsIHNvb24uDQoNCjIuIFRvIGFkZCBhIHBhZ2UgdG8gYSBuYXZpZ2F0aW9uIGJhciAoYWxzbyBjYWxsZWQgYSBuYXZiYXIpIHRoZSBmb2xsb3dpbmcgc3ludGF4IGlzIHVzZWQgYD09PT09PT1gLiBUaGUgbnVtYmVyIG9mIGRhc2hlcyBkb2VzIG5vdCBtYXR0ZXIuIChUaGlzIGlzIGEgbGV2ZWwgMSBoZWFkZXIgaW4gTWFya2Rvd24sIGp1c3QgbGlrZSBgI2ApDQoNCjMuIFRvIGFkZCBjb2x1bW5zIG9yIHJvd3MgdGhlIGZvbGxvd2luZyBzeW50YXggaXMgdXNlZCBgLS0tLS0tLS0tYC4gQnkgZGVmYXVsdCB0aGlzIG5vdGF0aW9uIHdpbGwgY3JlYXRlIG5ldyBjb2x1bW5zLCBob3dldmVyIGlmIHRoZSBZQU1MIGlzIG1vZGlmaWVkIHRvIHNwZWNpZnkgdG8gY3JlYXRlIHJvd3MsIHRoYW4gdGhpcyBzYW1lIHN5bnRheCB3aWxsIGJlIHVzZWQgdG8gY3JlYXRlIHJvd3MuIFRoZSBudW1iZXIgb2YgZGFzaGVzIGRvZXMgbm90IG1hdHRlci4gKFRoaXMgaXMgYSBsZXZlbCAyIGhlYWRlciBpbiBNYXJrZG93biwganVzdCBsaWtlIGAjI2ApDQoNCjQuIENvbXBvbmVudHMgd2l0aGluIHRoZSBkYXNoYm9hcmQgYXJlIGRlbGluZWF0ZWQgYnkgdXNpbmcgYCMjI2AgLSBpZiB5b3UgYXJlIGZhbWlsaWFyIHdpdGggTWFya2Rvd24gbm90YXRpb24sIHRoaXMgaXMgYSBsZXZlbCAzIE1hcmtkb3duIGhlYWRlci4NCg0KSWYgdGhpcyBpbmNsdWRlcyB0ZXh0IGxpa2Ugc286IGAjIyMgdGV4dGAsIHRoaXMgYWRkcyBoZWFkZXIgdGV4dCB0byB0aGUgY29tcG9uZW50LCBob3dldmVyIHRoaXMgaXMgbm90IHJlcXVpcmVkLiANCg0KDQo1LiBUbyAgaW5jbHVkZSBhIHBsb3Qgb3IgYW55IG91dHB1dCBmcm9tIFIsIHVzZSB0aGUgZm9sbG93aW5nIHN5bnRheDoNCmAiYGBge3J9ImAgb24gaXQncyBvd24gbGluZSBmb2xsb3dlZCBieSB5b3VyIGNvZGUsIGZvbGxvd2VkIGJ5IGAiYGBgImAuIFRoaXMgY3JlYXRlcyB3aGF0IGlzIGNhbGxlZCBhIGNvZGUgY2h1bmsuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICIzMCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImNvZGVfY2h1bmsucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL291cmNvZGluZ2NsdWIuZ2l0aHViLmlvL3R1dG9yaWFscy9ybWFya2Rvd24vKQ0KDQo2LiBBbm90aGVyIGNvbXBvbmVudCBvZiBgZmxleGRhc2hib2FyZGAgaXMgdmFsdWUgYm94ZXMuIFRoZXNlIGFyZSBlc3NlbnRpYWxseSB0ZXh0IGJveGVzIGZvciBzdGF0aXN0aWNzIG9yIHRleHQgdGhhdCB5b3UgbWlnaHQgbGlrZSB0byBmZWF0dXJlIG9yIGVtcGhhc2l6ZS4gVG8gZG8gdGhpcyBhZ2FpbiB0aGUgYCMjI2Agc3ludGF4IGlzIHVzZWQgdG8gcHV0IGEgdGV4dCBsYWJlbCBkZXNjcmliaW5nIHdoYXQgdGhlIHZhbHVlIGJveCBjb250YWlucyBmb2xsb3dlZCBieSBhIGNvZGUgY2h1bmsgdGhhdCB1c2VzIHRoZSBgdmFsdWVCb3goKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGZsZXhkYXNib2FyZGAgcGFja2FnZS4gVGhlIHZhbHVlIHRvIGRpc3BsYXkgaXMgc3BlY2lmaWVkIHVzaW5nIHRoZSBgdmFsdWVgIGFyZ3VtZW50LCBhcyB3ZWxsIGFzIG9wdGlvbmFsIG90aGVyIGFzcGVjdHMgdXNpbmcgYWRkaXRpb25hbCBhcmd1bWVudHMsIHN1Y2ggYXMgdGhlIGNvbG9yIG9mIHRoZSB2YWx1ZSBib3ggdXNpbmcgdGhlIGBjb2xvcmAgYXJndW1lbnQgbGlrZSB0aGUgZXhhbXBsZSBiZWxvdzoNCg0KYGBgDQojIyMgVmFsdWVCb3hUZXh0DQoNCicnJ3tyfQ0KdmFsdWVCb3godmFsdWUgPSAxMA0KICBjb2xvciA9ICJ3aGl0ZSIpDQoNCicnJw0KYGBgDQoNCioqTm90ZSoqOiBpbiBvdXIgZXhhbXBsZXMgb2YgY29kZSB3ZSB3aWxsIHVzZSBgIicnJyJgIGluc3RlYWQgb2YgYCJgYGAiYC4gVGhpcyBpcyBvbmx5IHRvIGFsbG93IGZvciBlYXN5IHZpZXdpbmcgb2YgZXhhbXBsZXMuIEFsbCBjb2RlIGNodW5rcyByZXF1aXJlIGAiYGBgImAuDQoNCkhlcmUgeW91IGNhbiBzZWUgYSBtb3JlIHRob3JvdWdoIGV4YW1wbGUgd2hpY2ggaW5jbHVkZXMgaWNvbnM6DQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJ2YWx1ZWJveC5wbmciKSkNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvdXNpbmcuaHRtbCN2YWx1ZV9ib3hlcykNCg0KNy4gSW5zdGVhZCBvZiB2YWx1ZSBib3hlcyB5b3UgY2FuIGFsc28gaW5jbHVkZSBhIHNsaWdodCB2YXJpYXRpb24gY2FsbGVkIGEgZ2F1Z2UuIFRoZXNlIGFyZSBjcmVhdGVkIHdpdGggdGhlIGBndWFnZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4gVGhpcyByZXF1aXJlcyBudW1lcmljIHZhbHVlcyBmb3IgYSBgdmFsdWVgLCBhIGBtaW5gLCBhbmQgYSBgbWF4YCBhcmd1bWVudC4gT3B0aW9uYWxseSwgYSBzeW1ib2wgY2FuIGFsc28gYmUgYWRkZWQgd2l0aCB0aGUgYHN5bWJvbGAgYXJndW1lbnQuIFRoZSB2YWx1ZSBhcmd1bWVudCBkb2VzIG5vdCBoYXZlIHRvIGJlIGV4cGxpY2l0bHkgY2FsbGVkIHRob3VnaCwgd2hpY2ggaXMgYWxzbyB0cnVlIG9mIHRoZSBgdmFsdWVCb3goKWAgZnVuY3Rpb24uDQoNCkhlcmUgaXMgYSBzaW1wbGUgZXhhbXBsZToNCg0KYGBgDQojIyMgR3VhZ2VUZXh0DQoNCicnJ3tyfQ0KZmxleGRhc2hib2FyZDo6Z2F1Z2UodmFsdWUgPSAxMCwgDQogICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICBtYXggPSAxMDAsIA0KICAgICAgICAgICAgICAgICAgICBzeW1ib2wgPSAiJSIpDQoNCicnJw0KYGBgDQoNClRoaXMgY3JlYXRlcyB0aGUgZm9sbG93aW5nIG91dHB1dDoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZ2F1Z2Vfb3V0cHV0LnBuZyIpKQ0KYGBgDQoNCkhlcmUgaXMgYSBtb3JlIGNvbXBsaWNhdGVkIGV4YW1wbGU6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZ2F1Z2UucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL3VzaW5nLmh0bWwjdmFsdWVfYm94ZXMpDQoNCiMjICoqTGF5b3V0KioNCioqKg0KDQojIyMgKipBZGRpbmcgQ29sdW1ucyoqDQoqKioNCg0KVG8gYWRkIG11bHRpcGxlIGNvbHVtbnMgdGhlIGZvbGxvd2luZyBzeW50YXggaXMgdXNlZCBgLS0tLS0tLS0tYCBmb3IgZWFjaCAgY29sdW1uIGFuZCBub3RoaW5nIGFkZGl0aW9uYWwgaXMgcmVxdWlyZWQgaW4gdGhlIGhlYWRlci4NCg0KQWRkaXRpb25hbCBmZWF0dXJlcyBhYm91dCB0aGUgY29sdW1ucywgc3VjaCBhcyB0aGUgd2lkdGggY2FuIGJlIHNwZWNpZmllZCB1c2luZyBicmFja2V0c2B7fWBsaWtlIGluIHRoZSBleGFtcGxlIGJlbG93LiBOb3RlIHRoYXQgdGhlIHdvcmQgYENvbHVtbmAgaXNuJ3QgbmVjZXNzYXJ5LiBJbiB0aGlzIGV4YW1wbGUgdHdvIGNvbHVtbnMgYXJlIGNyZWF0ZWQgdGhhdCB3aWxsIGJlIG9yaWVudGVkIG5leHQgdG8gb25lIGFub3RoZXIgYW5kIGVsZW1lbnRzIHdpdGhpbiB0aGUgY29sdW1ucyB3aWxsIGJlIHBsYWNlZCB0b3AgdG8gYm90dG9tLiANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJjb2x1bW5zLnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZmxleGRhc2hib2FyZC9sYXlvdXRzLmh0bWwpDQoNCg0KIyMjICoqQWRkaW5nIFJvd3MqKg0KKioqDQoNClRvIGFkZCBtdWx0aXBsZSByb3dzIC0gdGhlIHlhbWwgbmVlZHMgdG8gc3RhdGUgdGhhdCB0aGUgb3JpZW50YXRpb24gaXMgZm9yIHJvd3MgaW5zdGVhZCBvZiBmb3IgY29sdW1ucyAoc2VlIHRoZSBpbWFnZSBiZWxvdyksICBhbmQgdGhlbiB0aGUgc2FtZSBzeW50YXggaXMgdXNlZCBgLS0tLS0tLS0tYCAgZm9yIGVhY2ggcm93IGluc3RlYWQgb2YgY29sdW1ucy4gSW4gdGhpcyBleGFtcGxlLCB0d28gcm93cyBhcmUgY3JlYXRlZCB0aGF0IHdpbGwgYmUgb3JpZW50ZWQgb24gdG9wIG9mIG9uZSBhbm90aGVyIGFuZCBlbGVtZW50cyB3aXRoaW4gdGhlIHJvd3Mgd2lsbCBiZSBwbGFjZWQgbmV4dCB0byBlYWNoIG90aGVyLg0KDQpBZ2FpbiB0aGUgd29yZCBgUm93YCBpcyBub3QgYWN0dWFsbHkgbmVjZXNzYXJ5Lg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInJvd3MucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL2xheW91dHMuaHRtbCkNCg0KU2VlIFtoZXJlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL2xheW91dHMuaHRtbCkgZm9yIHRlbXBsYXRlIG9wdGlvbnMuDQoNCg0KIyMjICoqVGFicyoqDQoqKioNCg0KVG8gYWRkIHRhYnMgY29sdW1ucy9yb3dzIHdlIGNhbiB1c2UgdGhlIGZvbGxvd2luZzogDQoNCmBgYA0KQ29sdW1uIHsudGFic2V0fQ0KYGBgDQoNCkluIHRoaXMgZXhhbXBsZSwgdHdvIGNvbHVtbnMgYXJlIGNyZWF0ZWQgYW5kIHRoZW4gdHdvIHRhYnMgYXJlIGFkZGVkIHRvIHRoZSBzZWNvbmQgY29sdW1uLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInRhYi5wbmciKSkNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvbGF5b3V0cy5odG1sKQ0KDQojIyMgKipTaGlueSoqDQoqKioNCg0KSW50ZXJhY3RpdmUgZWxlbWVudHMgY2FuIGJlIGFkZGVkIHRvIGRhc2hib2FyZHMuIEluIG91ciBkYXNoYm9hcmQsIHdlIHdpbGwgdXNlIHBhY2thZ2VzIHN1Y2ggYXMgYERUYCBhbmQgYGxlYWZsZXRgIHRoYXQgaGF2ZSBzaGlueSBmdW5jdGlvbmFsaXR5LiBUaGlzIHJlcXVpcmVzIHRoYXQgc2hpbnkgaXMgZW5hYmxlZCBpbiB0aGUgWUFNTCBoZWFkZXIgYnkgaW5jbHVkaW5nIGBydW50aW1lOnNoaW55YCBpbiB0aGUgWUFNTC4NCg0KSGVyZSBpcyBhbiBleGFtcGxlIG9mIGEgWUFNTCB0aGF0IGluY2x1ZGVzIHRoaXM6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICIzMCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInNoaW55X3ltbC5wbmciKSkNCmBgYA0KDQoqKioNCg0KIyMgKipEZXBsb3ltZW50KioNCioqKg0KDQpZb3UgaGF2ZSBhIGZldyBvcHRpb25zIHRvIHB1Ymxpc2ggeW91ciBkYXNoYm9hcmQ6ICANCg0KMSkgSWYgeW91ciBkYXNoYm9hcmQgaXMgbm90IGludGVyYWN0aXZlIChkb2VzIG5vdCB1c2Ugc2hpbnkpIG9yIHVzZXMgY2VydGFpbiB3aWRnZXRzIGxpa2UgdGhlIGBkYXRhdGFibGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYERUYCBwYWNrYWdlLCB5b3UganVzdCBuZWVkIHRvIGtuaXQgeW91ciBSIE1hcmtkb3duIGZpbGUgaW50byBhbiBodG1sIGZpbGUuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNTAlIiwgZmlnLmFsaWduPSJjZW50ZXIifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImtuaXQucG5nIikpDQpgYGANCg0KVGhlbiB5b3UgY2FuIGhvc3QgdGhpcyBvbiBHaXRIdWIgaWYgeW91IGNob29zZSBieSBjaGFuZ2luZyB0aGUgR2l0SHViIFBhZ2VzIHNldHRpbmdzIG9mIHlvdXIgcmVwb3NpdG9yeToNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnaXRodWJwYWdlcy5wbmciKSkNCmBgYA0KDQoyKSBJZiB5b3VyIGRhc2hib2FyZCBpcyBpbnRlcmFjdGl2ZSAodXNlcyBzaGlueSksIHlvdSBjYW4gaG9zdCBpdCBvbiAgaHR0cHM6Ly93d3cuc2hpbnlhcHBzLmlvLyBhZnRlciBtYWtpbmcgYW4gYWNjb3VudC4gVG8gZG8gdGhpcyB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSBgcnNjb25uZWN0YCBwYWNrYWdlIGFuZCBhZnRlciB5b3UgaGF2ZSBtYWRlIGFuIGFjY291bnQgYW5kIGNvbmZpZ3VyZWQgaXQsIHlvdSBjYW4gdXNlIHRoZSBwdWJsaXNoIGJ1dHRvbiBvZiB0aGUgUlN0dWRpbyBJREUgd2hpY2ggbG9va3MgbGlrZSB0aGlzIG9uIHRoZSB1cHBlciByaWdodCBjb3JuZXI6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInB1Ymxpc2gucG5nIikpDQpgYGANCg0KTm90ZSB0aGF0IHRoaXMgYWxzbyByZXF1aXJlcyBhdXRoZW50aWNhdGlvbiB1c2luZyB0b2tlbnMuIA0KDQpTZWUgdGhpcyBbbGlua10oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9hcnRpY2xlcy9zaGlueWFwcHMuaHRtbCkgZm9yIGEgZ2V0dGluZyBzdGFydCBmb3IgdGhpcyBwcm9jZXNzLg0KDQozKSBZb3UgY2FuIGFsc28gcHVibGlzaCB1c2luZyBbUlN0dWRpbyBDb25uZWN0XShodHRwczovL3JzdHVkaW8uY29tL3Byb2R1Y3RzL2Nvbm5lY3QvKS4gVGhpcyBhbHNvIGludm9sdmVzIGNyZWF0aW5nIGFuIGFjY291bnQgYW5kIHB1c2hpbmcgdGhlIHB1Ymxpc2ggYnV0dG9uLiANCg0KDQojIyMjIHsucmVjYWxsX2NvZGVfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KTGV0J3MgdGFrZSBhIG1pbnV0ZSB0byB0ZXN0IHlvdXIga25vd2xlZGdlIGFib3V0IGBmbGV4ZGFzaGJvYXJkYCBiYXNpY3M6ICANCg0KMSkgSG93IGRvIHdlIGNyZWF0ZSBtdWx0aXBsZSBwYWdlcz8gIA0KMikgSG93IGRvIHdlIGNyZWF0ZSBtdWx0aXBsZSBjb2x1bW5zPyAgDQozKSBIb3cgZG8gd2UgY3JlYXRlIG11bHRpcGxlIHRhYnM/ICANCjQpIEhvdyBkbyB3ZSBzdGFydCBjcmVhdGluZyBhIGRhc2hib2FyZD8gIA0KNSkgSG93IGRvIHdlIGVuYWJsZSBvdXIgZGFzaGJvYXJkIHRvIGJlIGludGVyYWN0aXZlPyAgDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgYW5zd2Vycy4gPC9zdW1tYXJ5Pg0KDQoxKSBIb3cgZG8gd2UgY3JlYXRlIG11bHRpcGxlIHBhZ2VzPyBXZSB1c2UgdGhlIGA9PT1gIHN5bnRheC4NCjIpIEhvdyBkbyB3ZSBjcmVhdGUgbXVsdGlwbGUgY29sdW1ucz8gV2UgdXNlIHRoZSBgLS0tYCBzeW50YXguDQozKSBIb3cgZG8gd2UgY3JlYXRlIG11bHRpcGxlIHRhYnM/IFdlIHVzZSBgey50YWJzZXR9YCBzeW50YXggY29tYmluZWQgd2l0aCB0aGUgY29sdW1uIGJyZWFrIGAtLS1gIHN5bnRheC4gDQo0KSBIb3cgZG8gd2Ugc3RhcnQgY3JlYXRpbmcgYSBkYXNoYm9hcmQ/IFdlIGNyZWF0ZSBhbiBSIE1hcmtkb3duIGRvY3VtZW50IGFuZCB3ZSBhZGQgYG91dHB1dDogZmxleGRhc2hib2FyZDo6ZmxleGRhc2hib2FyZGAgdG8gdGhlIFlBTUwuDQo1KSBIb3cgZG8gd2UgZW5hYmxlIG91ciBkYXNoYm9hcmQgdG8gYmUgaW50ZXJhY3RpdmU/ICBXZSBhZGQgYHJ1bnRpbWU6c2hpbnlgIHRvIHRoZSBZQU1MLg0KDQo8L2RldGFpbHM+DQoNCioqKg0KDQojIyMjDQoNCg0KDQojICoqT3VyIERhc2hib2FyZCoqDQoqKioNCg0KT0shIE5vdyB0aGF0IHdlIGtub3cgYSBiaXQgYWJvdXQgdGhlIGJhc2ljcyBvZiBjcmVhdGluZyBhIGRhc2hib2FyZCwgbGV0J3MgY3JlYXRlIG91ciBvd24uDQoNClRoZSBsaW5rIHRvIHRoZSBkYXNoYm9hcmQgZGVzY3JpYmVkIGluIHRoaXMgc2VjdGlvbiBpcyBsb2NhdGVkIFtoZXJlXShodHRwczovL3JzY29ubmVjdC5iaW9zdGF0Lmpoc3BoLmVkdS9vY3MtYnAtc2Nob29sLXNob290aW5ncy1kYXNoYm9hcmQvKS4NCg0KV2Ugd2FudCB0byBjcmVhdGUgYSBkYXNoYm9hcmQgdGhhdCBoYXMgc2V2ZXJhbCB0YWJzIHRoYXQgd2lsbCBsb29rIGxpa2UgdGhpczoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmxpbmsgPSAiaHR0cHM6Ly9yc2Nvbm5lY3QuYmlvc3RhdC5qaHNwaC5lZHUvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkLyJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZGFzaGJvYXJkX3NjaG9vbC5wbmciKSkNCmBgYA0KDQojIyAqKkdldHRpbmcgc3RhcnRlZCoqDQoqKioNCg0KVGhlIGZpcnN0IHRoaW5nIHdlIG5lZWQgdG8gZG8gdG8gY3JlYXRlIG91ciBkYXNoYm9hcmQgaXMgdG8gY3JlYXRlIGEgbmV3IC5SbWQgZG9jdW1lbnQgbGlrZSBzbyBpbiBSIFN0dWRpbzoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJuZXdybWQucG5nIikpDQpgYGANCg0KDQojIyAqKllBTUwgaGVhZGVyKioNCioqKg0KDQpOZXh0IHdlIG5lZWQgdG8gdXBkYXRlIHRoZSBZQU1MIGhlYWRlciB0byBsb29rIGxpa2UgdGhpczoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJvdXJfeWFtbC5wbmciKSkNCmBgYA0KDQoNCkFzIHlvdSBtaWdodCBleHBlY3QsIGB0aXRsZTpgIGluZGljYXRlcyB0aGUgdGl0bGUgb2Ygb3VyIGRhc2hib2FyZC4NCg0KVGhlIGBvdXRwdXQ6YCBsaW5lIHNwZWNpZmllcyB3aGF0IHR5cGUgb2Ygb3V0cHV0IHdlIHdhbnQgdGhlIC5SbWQgZmlsZSB0byBiZSByZW5kZXJlZC4NCg0KV2UgbmVlZCB0byBpbmNsdWRlIGBmbGV4ZGFzaGJvYXJkOjpmbGV4X2Rhc2hib2FyZDpgIGFzIHRoZSBvdXRwdXQgdG8gY3JlYXRlIGEgZGFzaGJvYXJkIHdpdGggdGhlIGBmbGV4ZGFzaGJvYXJkYCBwYWNrYWdlLiBUaGlzIGNhbiBiZSBpbmNsdWRlZCBvbiB0aGUgc2FtZSBsaW5lIGFzIGBvdXRwdXQ6YCBvciBvbiB0aGUgbmV4dCBsaW5lIHdpdGggYSBwcmVjZWRpbmcgdGFiLg0KDQoqKk5vdGUqKjogdGhhdCBZQU1MIGlzIHNlbnNpdGl2ZSB0byBzcGFjaW5nLCB0aHVzIHRoaXMgdGFiIGlzIHJlcXVpcmVkIHRvIGdldCB0aGUgcHJvcGVyIG91dHB1dC4NCg0KVGhlIG5leHQgZm91ciBsaW5lcyBhcmUgYXJndW1lbnRzIGZvciBob3cgdGhlIGRhc2hib2FyZCBzaG91bGQgYmUgY3JlYXRlZC4NCg0KMS4gYGxvZ286YCBhbGxvd3MgeW91IHRvIGluY2x1ZGUgYSBsb2dvIG9uIHRvcCBvZiB5b3VyIGRhc2hib2FyZC4gV2l0aCB0aGlzIHRoZW1lIHRoaXMgd2lsbCBiZSBpbiB0aGUgdXBwZXIgbGVmdCBjb3JuZXIuIFRoZSBsb2dvIHdlIGNob3NlIHRvIHVzZSBjYW1lIGZyb20gW2hlcmVdKGh0dHBzOi8vaWNvbmFyY2hpdmUuY29tLyksIGJ1dCB5b3UgY291bGQgdGhlb3JldGljYWxseSB1c2UgYW55IHBuZyBvZiBhcHByb3ByaWF0ZSBzaXplLg0KDQoyLiBgdGhlbWU6YCBhbGxvd3MgeW91IHRvIHNwZWNpZnkgaG93IHRoZSBkYXNoYm9hcmQgd2lsbCBsb29rIGluIGdlbmVyYWwuIE5vdGUgdGhhdCB0aGlzIGNhbiBiZSB1c2VkIHRvIG1vZGlmeSB0aGUgZ2VuZXJhbCBsb29rIG9mIGFueSB0eXBlIG9mIFIgTWFya2Rvd24gb3V0cHV0LCBub3QganVzdCBkYXNoYm9hcmRzIGNyZWF0ZWQgd2l0aCBgZmxleGRhc2hib2FyZGAuIFNlZSBbaGVyZV0oaHR0cHM6Ly93d3cuZGF0YWRyZWFtaW5nLm9yZy9wb3N0L3ItbWFya2Rvd24tdGhlbWUtZ2FsbGVyeS8pIGZvciBhIGxpc3Qgb2Ygb3B0aW9ucy4gSW4gb3VyIGNhc2UsIHRoZSB0aGVtZSBpcyBjYWxsZWQgcmVhZGFibGUgYW5kIHdpbGwgY3JlYXRlIGRvY3VtZW50cyB0aGF0IGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInJlYWRhYmxlLnBuZyIpKQ0KYGBgDQoNCiMjIyMjIFtbc291cmNlXV0oaHR0cHM6Ly93d3cuZGF0YWRyZWFtaW5nLm9yZy9wb3N0L3ItbWFya2Rvd24tdGhlbWUtZ2FsbGVyeS8pDQoNCjMuIGBvcmllbnRhdGlvbjpgIHRoZSBvcHRpb25zIGFyZSBgY29sdW1uc2Agb3IgYHJvd3NgIGFuZCBzcGVjaWZpZXMgaWYgdGhlIGAtLS0tLS0tLWAgc3ludGF4IGNyZWF0ZXMgcm93cyBvciBjb2x1bW5zIGZvciB0aGUgbGF5b3V0LiBUaGlzIGlzIG5vdCBuZWNlc3NhcnkgaWYgdGhlIG9wdGlvbiBpcyBgY29sdW1uc2AuDQoNCjQuIGBzb3VyY2VfY29kZTpgIHNwZWNpZmllcyBpZiBhIFVSTCB3aWxsIGJlIGluY2x1ZGVkIGFzIGEgbmF2aWdhdGlvbiBiYXIgaXRlbSB3aXRoIGFjY2VzcyB0byB0aGUgc291cmNlIGNvZGUuIA0KDQo1LiBgdmVydGljYWxfbGF5b3V0OmAgVGhlIG9wdGlvbnMgYXJlIGBmaWxsYCBvciBgc2Nyb2xsYC4gRmlsbCBjYXVzZXMgdGhlIGNoYXJ0cyB0byByZS1zaXplIHRvIGZpbGwgdGhlIHBhZ2UsIHdoaWxlIHRoZSBzY3JvbGwgb3B0aW9uIHJlbmRlcnMgcGxvdHMgYXMgdGhlaXIgbmF0dXJhbCBoZWlnaHQgd2hpY2ggbWF5IG9yIG1heSBub3QgcmVxdWlyZSBzY3JvbGxpbmcgdGhlIHBhZ2UuDQoNClRoZXJlIGFyZSBtYW55IG90aGVyIGFyZ3VtZW50IG9wdGlvbnMgZm9yIGhvdyB0aGUgZGFzaGJvYXJkIGlzIGRpc3BsYXllZC4NCg0KWW91IGNhbiBydW4gdGhlIGZvbGxvd2luZyBjb21tYW5kIGluIHRoZSBjb25zb2xlIHRvIHNlZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBhcmd1bWVudHMgaW4gdGhlIGhlbHAgcGFuZSBvZiB0aGUgUiBTdHVkaW8gW0lERV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSW50ZWdyYXRlZF9kZXZlbG9wbWVudF9lbnZpcm9ubWVudCkuDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KP2ZsZXhkYXNoYm9hcmQ6OmZsZXhfZGFzaGJvYXJkKCkNCmBgYA0KDQpBbHNvIHNlZSB0aGUgQ1JBTiBbZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2ZsZXhkYXNoYm9hcmQvZmxleGRhc2hib2FyZC5wZGYpIGZvciBtb3JlIGRldGFpbHMuDQoNCiMjICoqTG9hZGluZyB0aGUgcGFja2FnZXMgYW5kIGRhdGEqKg0KKioqDQoNClNpbmNlIHdlIGFyZSBjcmVhdGluZyBvdXIgZGFzaGJvYXJkIGluIGEgbmV3IFJtZCBmaWxlLCB3ZSBuZWVkIHRvIGxvYWQgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBhbmQgdGhlIHdyYW5nbGVkIGRhdGEgdGhhdCB3ZSBjcmVhdGVkIGluIHRoaXMgUm1kIGZpbGUuIEluIHRoYXQgUm1kIGZpbGUsIGl0IGxvb2tzIHNvbWV0aGluZyBsaWtlIHRoaXMuIA0KDQoqKk5vdGUqKjogYWxsIHRoZSBmb2xsb3dpbmcgY29kZSB3b3VsZCBiZSAqKmFkZGVkIHRvIHRoZSBSbWQgZmlsZSBmb3IgdGhlIGRhc2hib2FyZCoqIGFuZCBhcmUgc2ltcGx5IHNob3duIGhlcmUgZm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3Nlcy4NCg0KYGBge3IsZXZhbCA9IEZBTFNFfQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGZsZXhkYXNoYm9hcmQpDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoZm9yY2F0cykNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkod2FmZmxlKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocG9saXNjaWRhdGEpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGh0bWx0b29scykNCmxpYnJhcnkoRFQpDQpgYGANCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgd2hhdCB0aGVzZSBwYWNrYWdlcyB3ZXJlIHVzZWQgZm9yLCBzZWUgdGhlIGJlZ2lubmluZyBvZiB0aGlzIGNhc2Ugc3R1ZHkgYW5kIHRoZSBbSGVscGZ1bCBMaW5rc10gc2VjdGlvbi4gVGhlIGRhdGEgY2FuIGJlIGZvdW5kIGFuZCBkb3dubG9hZGVkIGZyb20gb3VyIEdpdEh1YiByZXBvc2l0b3J5IGF0IHRoaXMgW2xpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkL3RyZWUvbWFzdGVyL2RhdGEvd3JhbmdsZWQpLiBJbiBvdXIgY2FzZSB3ZSBzYXZlZCB0aGlzIHRvIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCBgd3JhbmdsZWRgIHdpdGhpbiBhIGRpcmVjdG9yeSBjYWxsZWQgYGRhdGFgIG9mIG91ciB3b3JraW5nIGRpcmVjdG9yeS4gV2UgcmVjb21tZW5kIHVzaW5nIFJTdHVkaW8gcHJvamVjdHMgYW5kIHRoZSBgaGVyZWAgcGFja2FnZSB0byBtYWtlIG5hdmlnYXRpbmcgdG8gZmlsZXMgZWFzeSBhbmQgcmVwcm9kdWNpYmxlLg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0Kc2hvb3RpbmdfZGF0YSA8LSANCiAgcmVhZF9jc3YoaGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsDQogICAgICAgICAgICAgICAgInNob290aW5nX2RhdGFfd3JhbmdsZWRfcHJlX21hcC5jc3YiKSkNCg0Kc2hvb3RpbmdfZGF0YV9mb3JfbWFwIDwtIA0KICByZWFkX2NzdihoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwNCiAgICAgICAgICAgICAgICAic2hvb3RpbmdfZGF0YV93cmFuZ2xlZF9mb3JfbWFwLmNzdiIpKQ0KYGBgDQoNCg0KIyMgKipDcmVhdGluZyBwYWdlcyoqDQoqKioNCg0KUmVjYWxsIHRoYXQgYD09PWAgaXMgdXNlZCB0byBkZXNpZ25hdGUgZWxlbWVudHMgdGhhdCBhcmUgcGFydCBvZiB0aGUgbmF2aWdhdGlvbiBiYXIuDQoNCldlIHdhbnQgNyBpdGVtcyBiZXNpZGVzIHRoZSBzb3VyY2UgY29kZSAod2hpY2ggd2FzIGFkZGVkIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gdGhlIFlBTUwgY29kZSkuDQoNCkZpcnN0LCB3ZSBjcmVhdGUgNyBkaXZpc2lvbnMgZm9yIHRoZXNlIG1haW4gcGFnZXMuIFdlIGFkZCBpY29ucyB0byBlYWNoIGZyb20gW0ZvbnQgQXdlc29tZV0oaHR0cHM6Ly9mb250YXdlc29tZS5jb20vaWNvbnM/ZD1nYWxsZXJ5KS4NCg0KVXNlIHRoaXMgW2xpbmtdKGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tL2ljb25zP2Q9Z2FsbGVyeSkgdG8gZmluZCBvdGhlciBpY29uIG9wdGlvbnMuIElmIHlvdSBjbGljayBvbiB0aGUgInN0YXJ0IHVzaW5nIHRoaXMgaWNvbiIgYnV0dG9uIGl0IHdpbGwgdGFrZSB5b3UgdG8gYSBwYWdlIHdpdGggSFRNTCBjb2RlIGxpa2UgdGhpczogIA0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZm9udGF3ZXNvbWUucG5nIikpDQpgYGANCg0KIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tL2ljb25zL2RhdGFiYXNlP3N0eWxlPXNvbGlkKQ0KDQpPbmx5IHRoZSBgZmEtZGF0YWJhc2VgIHBvcnRpb24gaXMgcmVxdWlyZWQgaW4gdGhlIGJyYWNrZXRzIGFmdGVyIGBkYXRhLWljb249YCB0byBhZGQgdGhlIGljb24gdG8gdGhlIG5hdmlnYXRpb24gYmFyLg0KDQpgYGANCg0KQWJvdXQge2RhdGEtaWNvbj0iZmEtcXVlc3Rpb24tY2lyY2xlIn0NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpUaGUgRGF0YSB7ZGF0YS1pY29uPSJmYS1kYXRhYmFzZSJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IA0KDQpVUyBTdGF0aXN0aWNzIHtkYXRhLWljb249ImZhLWZsYWcifQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpTdGF0ZSBTdGF0aXN0aWNzIHtkYXRhLWljb249ZmEtZmxhZy1jaGVja2VyZWR9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KTWFwIHtkYXRhLWljb249ImZhLW1hcCJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KVHV0b3JpYWwgey5zdG9yeWJvYXJkIGRhdGEtaWNvbj0iZmEtbGlzdC1vbCJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KSG90bGluZSB7ZGF0YS1pY29uPSJmYS1leGNsYW1hdGlvbi10cmlhbmdsZSJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KYGBgDQoNCiMjICoqVGhlIEFib3V0IFBhZ2UqKg0KKioqDQoNCkhlcmUsIHdlIGNyZWF0ZSBjb250ZW50IGluIHRoZSBBYm91dCBwYWdlLg0KDQojIyMgKipMb29rKioNCioqKg0KDQpUaGlzIGlzIHdoYXQgdGhlIHBhZ2Ugd2lsbCBsb29rIGxpa2U6DQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiAsICJhYm91dHBhZ2Vsb29rLnBuZyIpKQ0KYGBgDQoNCiMjIyAqKk92ZXJhbGwgU3RydWN0dXJlKioNCioqKg0KDQpIZXJlIGlzIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBmb3IgdGhpcyBwYWdlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAiYWJvdXRfcGFnZV9zdHJ1Y3R1cmUucG5nIikpDQpgYGANCg0KIyMjICoqRGV0YWlscyoqDQoqKioNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIGlmIHlvdSB3b3VsZCBsaWtlIHRvIHNlZSBhbGwgb2YgdGhlIGNvZGUgZm9yIHRoaXMgcGFnZS4gPC9zdW1tYXJ5Pg0KDQpPbiB0aGlzIHBhZ2Ugd2Ugd2lsbCBoYXZlIHR3byBjb2x1bW5zIC0gb25lIHdoaWNoIHdpbGwgYmUgd2lkZXIgdGhhbiB0aGUgb3RoZXIuICBTaXplIHNwZWNpZmljYXRpb25zIG9uIGBmbGV4ZGFzaGJvYXJkYCBhcmUgdW5pdC1sZXNzOyB0aGUgd2lkdGggb2YgYW55IGNvbHVtbiBpbmNsdWRlZCBvbiBhIHBhZ2UgaXMgYSBmdW5jdGlvbiBvZiB0aGUgd2lkdGggc2V0IGZvciBhIGNvbHVtbiBhZ2FpbnN0IHRoZSBzdW0gb2Ygd2lkdGhzIGZvciBhbGwgY29sdW1ucyBvbiB0aGF0IHBhZ2UuIElmIHdlIHNldCBjb2x1bW5zIHNpemVzIG9mIDYwMCBhbmQgMzAwIG9uIGEgcGFnZSB3aXRoIHR3byBjb2x1bW5zLCBvbmUgY29sdW1uIHdpbGwgYmUgdHdpY2UgYXMgbGFyZ2UgYXMgdGhlIG90aGVyIGNvbHVtbi4gV2Ugd2FudCB0aGUgbGVmdCBjb2x1bW4gdG8gYmUgcXVpdGUgYSBiaXQgbGFyZ2VyIHRoYW4gdGhlIHJpZ2h0LCBzbyB3ZSB3aWxsIHNldCB0aGUgbGVmdCBhcyBgNzBgIGFuZCB0aGUgcmlnaHQgYXMgYDMwYC4NCg0KDQojIyMjIHsucmVjYWxsX2NvZGVfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KQ2FuIHlvdSByZWNhbGwgaG93IHdlIHdvdWxkIG1ha2UgdGhlc2UgY29sdW1ucz8NCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+DQoNCg0KDQpgYGANCkFib3V0IHtkYXRhLWljb249ImZhLXF1ZXN0aW9uLWNpcmNsZSJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IA0KDQpDb2x1bW4ge2RhdGEtd2lkdGggPSA3MH0NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjDQoNCkNvbHVtbiB7ZGF0YS13aWR0aCA9IDMwfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMNCg0KYGBgDQoNCjwvZGV0YWlscz4NCg0KKioqDQoNCiMjIyMNCg0KUmVjYWxsIHRoYXQgYCMjI2AgaXMgdXNlZCB0byBhZGQgZWxlbWVudHMgdG8gY29sdW1ucyBhbmQgcm93cy4gTm90ZSB0aGF0IHRoZXJlIGlzIG5vIHRleHQgbmV4dCB0byB0aGUgYCMjI2Agc3ludGF4IHRoYXQgZGVzaWduYXRlcyBhbiBlbGVtZW50IG9mIG91ciBkYXNoYm9hcmQuIEluIHRoZSBwcmV2aW91cyBleGFtcGxlcywgYSBoZWFkZXIgd2FzIHVzZWQgbGlrZSBzbyBgIyMjIGhlYWRlcmA6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAidGFiLnBuZyIpKQ0KYGBgDQoNCltbc291cmNlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL2xheW91dHMuaHRtbCldDQoNCg0KV2UgZG8gbm90IGFjdHVhbGx5IHdhbnQgYSBoZWFkZXIgbm93LCBzbyB3ZSBjYW4gc2ltcGx5IHVzZSBgIyMjYCB3aXRob3V0IGFueSB0ZXh0IGZvbGxvd2luZyBpdC4gTm90ZSB0aGF0IHlvdSBjYW4gZ2V0IGF3YXkgd2l0aCBub3QgdXNpbmcgdGhlIGAjIyNgLCBidXQgc29tZSBlbGVtZW50cyB3aWxsIG5vdCByZW5kZXIgcHJvcGVybHkuIA0KDQpOZXh0LCB3ZSBhZGQgYSBibG9jayBvZiB0ZXh0IGRlc2NyaWJpbmcgdGhlIGRhc2hib2FyZCB0byB0aGUgZmlyc3QgY29sdW1uIGFuZCB3ZSB3aWxsIGFkZCBhbiBpbWFnZSB0byB0aGUgc2Vjb25kIGNvbHVtbiBsaWtlIHRoZSBmb2xsb3dpbmcuIE5vdGljZSB0aGF0IHR3byBhc3Rlcmlza3MgYCoqYCBhcm91bmQgdGV4dCBtYWtlcyB0aGVtIGFwcGVhciBhcyBib2xkIGFuZCBvbmUgYCpgIG1ha2VzIGl0IGFwcGVhciBhcyBpdGFsaWMuIFNlZSBbdGhpcyBSU3R1ZGlvIGNoZWF0c2hlZXRdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xNS5odG1sKSBmb3Igc29tZSBiYXNpYyBNYXJrZG93biBzeW50YXggZm9yIHN0eWxpemluZyB0ZXh0Og0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIm1hcmtkb3duc3ludGF4LnBuZyIpKQ0KYGBgDQoNCltbc291cmNlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9sZXNzb24tMTUuaHRtbCldIA0KDQoNClRoaXMgaXMgd2hhdCB0aGUgY29kZSBmb3IgdGhpcyBwYWdlIGxvb2tzIGxpa2UgKG5vdGljZSB0aGF0IHRoZXJlIGlzIGFuIGludGVybmFsIGxpbmsgdG8gdGhlIGBUdXRyaWFsYCBwYWdlKToNCg0KYGBgDQpBYm91dCB7ZGF0YS1pY29uPSJmYS1xdWVzdGlvbi1jaXJjbGUifQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSANCg0KQ29sdW1uIHtkYXRhLXdpZHRoPTcwfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgDQoNCioqV2hhdCBpcyB0aGUgcHVycG9zZSBvZiB0aGlzIGRhc2hib2FyZD8qKg0KDQpUaGlzIGRhc2hib2FyZCBoYXMgdHdvIHB1cnBvc2VzOg0KDQoxLiBUbyBpbGx1c3RyYXRlIHRyZW5kcyBpbiBzY2hvb2wgc2hvb3RpbmcgZXZlbnRzIGluIHRoZSBVbml0ZWQgU3RhdGVzDQoyLiBUbyBkZW1vbnN0cmF0ZSBob3cgdG8gY3JlYXRlIGEgZGFzaGJvYXJkIHVzaW5nIGBSYA0KDQoqKlRoZSBkYXRhKioNCg0KVGhpcyBkYXNoYm9hcmQgdXNlcyBkYXRhIGZyb20gdGhlIG9wZW4tc291cmNlIFtLLTEyIFNob29sIFNob290aW5nIERhdGFiYXNlXShodHRwczovL3d3dy5jaGRzLnVzL3NzZGIvZGF0YXNldC8pIGRvd25sb2FkZWQgZnJvbSB0aGUgW0NlbnRlciBmb3IgSG9tZWxhbmQgRGVmZW5zZSBhbmQgU2VjdXJpdHldKGh0dHBzOi8vd3d3LmNoZHMudXMvYy8pIGF0IHRoZSBhdCB0aGUgW05hdmFsIFBvc3RncmFkdWF0ZSBTY2hvb2woTlBTKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTmF2YWxfUG9zdGdyYWR1YXRlX1NjaG9vbCkuIFRoaXMgZGF0YSB3YXMgZG93bmxvYWRlZCBKdW5lIG9mIDIwMjAuDQoNCjxzdHlsZT4NCmRpdi5ncmVlbiB7IGJhY2tncm91bmQtY29sb3I6IzhGQkM4RjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4OyBmb250LXNpemU6IDFlbTtjb2xvcjogd2hpdGU7fQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAiZ3JlZW4iPg0KUmllZG1hbiwgRGF2aWQsIGFuZCBEZXNtb25kIE/igJlOZWlsbC4g4oCcQ0hEUyDigJMgSy0xMiBTY2hvb2wgU2hvb3RpbmcgRGF0YWJhc2Uu4oCdIENlbnRlciBmb3IgSG9tZWxhbmQgRGVmZW5zZSBhbmQgU2VjdXJpdHksIEp1bmUgMjAyMCwgW3d3dy5jaGRzLnVzL3NzZGJdKHd3dy5jaGRzLnVzL3NzZGIpLg0KPC9kaXY+DQoNCg0KICANClRoaXMgZGF0YWJhc2UgaW5jbHVkZXMgaW5mb3JtYXRpb24gYWJvdXQgc2Nob29sIHNob290aW5nIGV2ZW50cyBmb3Igc3R1ZGVudHMgaW4gZ3JhZGVzIEstMTIgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgZGF0aW5nIGJhY2sgdG8gMTk3MC4gVGhlIGRhdGFiYXNlIGhhcyBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIG5vdCBzaG93biBvbiBvdXIgZGFzaGJvYXJkIGluY2x1ZGluZywgYnV0IG5vdCBsaW1pdGVkIHRvOiBsb2NhdGlvbiBvZiB0aGUgZXZlbnQgYXQgdGhlIHNjaG9vbCwgc291cmNlIGZvciB0aGUgc2hvb3RpbmcgaW5mb3JtYXRpb24sIHNob290ZXIgY2hhcmFjdGVyaXN0aWNzLCBhbmQgdmljdGltIGNoYXJhY3RlcmlzdGljcy4gDQoNCiMjIyANCg0KDQo8dT4qKldhbnQgdG8gbGVhcm4gaG93IHRvIGNyZWF0ZSBhIGRhc2hib2FyZCBqdXN0IGxpa2UgdGhpcz8qKjwvdT4NCg0KVmlzaXQgdGhlIFsqVHV0b3JpYWwqXSgjdHV0b3JpYWwpIHBhZ2Ugb2YgdGhpcyBkYXNoYm9hcmQgdG8gZmlyc3QgbGVhcm4gdGhlIGJhc2ljcyBhYm91dCBidWlsZGluZyBhIGRhc2hib2FyZCB3aXRoIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4NCg0KQXQgdGhlIGVuZCBvZiB0aGUgdHV0b3JpYWwgd2UgcHJvdmlkZSBhIGxpbmsgdG8gdGhpcyBbc3VwcGxlbWVudGFyeSByZXNvdXJjZSBieSB0aGUgT3BlbiBDYXNlIFN0dWRpZXMgcHJvamVjdF0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC8pLCB3aGljaCBwcm92aWRlcyBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IGhvdyAqKip0aGlzIGRhc2hib2FyZCoqKiB3YXMgY3JlYXRlZC4NCg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDsgZm9udC1zaXplOiAuOCBlbTt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KICoqQWNrbm93bGVkZ2VtZW50cyoqDQoNClRoaXMgd2FzIGNyZWF0ZWQgYXMgcGFydCBvZiB0aGUgW09wZW4gQ2FzZSBTdHVkaWVzXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8pe3RhcmdldD0iX2JsYW5rIn0gcHJvamVjdC4gV2Ugd291bGQgbGlrZSB0byBhY2tub3dsZWRnZSB0aGUgW0Jsb29tYmVyZyBBbWVyaWNhbiBIZWFsdGggSW5pdGlhdGl2ZV0oaHR0cHM6Ly9hbWVyaWNhbmhlYWx0aC5qaHUuZWR1LykgZm9yIGZ1bmRpbmcgdGhpcyB3b3JrLiANCg0KICoqRGlzY2xhaW1lcioqDQoNClRoaXMgZGFzaGJvYXJkIHVzZXMgZGF0YSBmcm9tIHRoZSBbSy0xMiBTaG9vbCBTaG9vdGluZyBEYXRhYmFzZV0oaHR0cHM6Ly93d3cuY2hkcy51cy9zc2RiL2Fib3V0LykuIFdlIGFja25vd2xlZGdlIChsaWtlIHRoZWlyIHdlYnNpdGUpIHRoYXQgdGhlcmUgbWF5IGJlIHJlcG9ydGluZyBlcnJvcnMuIFRoZSB0cmVuZHMgYW5kIHN0YXRpc3RpY3Mgc2hvd24gZG8gbm90IGFjY291bnQgZm9yIHRoZSBtYW55IG90aGVyIGZhY3RvcnMgdGhhdCBtYXkgaW5mbHVlbmNlIHRoZSBvY2N1cnJlbmNlIG9mIHNob290aW5nIGV2ZW50cy4gVGhlIGRhc2hib2FyZCBzaG91bGQgbm90IGJlIHVzZWQgaW4gdGhlIGNvbnRleHQgb2YgbWFraW5nIHBvbGljeSBkZWNpc2lvbnMgd2l0aG91dCBleHRlcm5hbCBjb25zdWx0YXRpb24gZnJvbSBzY2llbnRpZmljIGV4cGVydHMuIA0KDQoNCiAqKkxpY2Vuc2UqKg0KDQpUaGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgdGhlIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uQ29tbWVyY2lhbCAzLjAgWyhDQyBCWS1OQyAzLjApXShodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMvMy4wL3VzLyl7dGFyZ2V0PSJfYmxhbmsifSBVbml0ZWQgU3RhdGVzIExpY2Vuc2UuDQo8L2Rpdj4NCg0KQ29sdW1uIHtkYXRhLXdpZHRoPTMwfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMNCg0KDQonJyd7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD0iW1Bob3RvZ3JhcGggYnkgTmF0aGFuIER1bWxhb10oaHR0cHM6Ly91bnNwbGFzaC5jb20vcGhvdG9zL3hQSG1tVktTOGxNKSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAibmF0aGFuLWR1bWxhby14UEhtbVZLUzhsTS11bnNwbGFzaC5qcGciKSkNCicnJw0KYGBgDQoNCk5vdGUgdGhhdCB3ZSB3aWxsIHVzZSBgIicnJyJgIGZvciB0byBzaG93IGNvZGUgY2h1bmtzIG9mIHRoZSBhY3R1YWwgY29kZSBmcm9tIHRoZSBkYXNoYm9hcmQuDQoNClRoZSBpbWFnZSB1c2VkIGluIHRoaXMgc2Vjb25kIGNvbHVtbiBpcyBmcm9tIGEgd2Vic2l0ZSBjYWxsZWQgdW5zcGxhc2ggKGh0dHBzOi8vdW5zcGxhc2guY29tLykgd2hpY2ggaG9zdHMgaW1hZ2VzIGZvciBmcmVlIHVzZSBidXQgaW5jbHVkZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHBob3RvZ3JhcGhlciBpZiB5b3UgY2hvc2UgdG8gY3JlZGl0IHRoZW0uIEEgc2hvcnQgbGluayBmb3IgdGhpcyBpbWFnZSB3YXMgZm91bmQgYnkgY2xpY2tpbmcgb24gaXQgYW5kIHRoZW4gY2xpY2tpbmcgdGhlIHNoYXJlIGJ1dHRvbi4NCg0KTm90aWNlIHRoZSBgZWNobyA9IEZBTFNFYCBzcGVjaWZpY2F0aW9uIGZvciB0aGUgY29kZSBjaHVuayB3aGljaCBjYXVzZXMgdGhlIGNvZGUgdG8gYmUgZXZhbHVhdGVkIGJ1dCBidXQgbm90IHNob3duLCB3aGlsZSBgZmlnLmNhcGAgYWRkcyB0aGUgZmlndXJlIGNhcHRpb24uDQoNClRoZSBpbWFnZSBpcyBpbmNsdWRlZCB1c2luZyB0aGUgYGluY2x1ZGVfZ3JhcGhpY3MoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGtuaXRyYCBwYWNrYWdlLiBXZSBuZWVkIHRvIHNwZWNpZnkgd2hlcmUgdGhpcyBpbWFnZSBpcyBsb2NhdGVkIGZvciB0aGlzIHRvIHdvcmsuIFlvdSBjYW4gZG8gdGhpcyB3aXRob3V0IHNwZWNpZnlpbmcgYSBwYXRoIGlmIHRoZSBpbWFnZSBmaWxlIGlzIGluIHRoZSBzYW1lIGRpcmVjdG9yeSBhcyB5b3VyIGAuUm1kYCBmaWxlIHRoYXQgeW91IGFyZSB1c2luZyB0byBjcmVhdGUgeW91ciBkYXNoYm9hcmQuIEhvd2V2ZXIgdXNpbmcgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBoZXJlYCBwYWNrYWdlIHdlIGNhbiBvcmdhbml6ZSBvdXIgZmlsZXMgYSBiaXQuIFRoaXMgZnVuY3Rpb24gd2lsbCBhdXRvbWF0aWNhbGx5IHN0YXJ0IHRoZSBwYXRoIHdoZXJldmVyIHdlIGhhdmUgaW5jbHVkZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGZpbGUsIHRoaXMgY2FuIGJlIGRvbmUgaW4gUlN0dWRpbyBsaWtlIHNvOg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiAsICJwcm9qZWN0LnBuZyIpKQ0KYGBgDQoNCklmIHlvdSBhcmUgbmV3IHRvIHVzaW5nIFJTdHVkaW8gcHJvamVjdHMsIHBsZWFzZSBzZWUgdGhpcyBbbGlua10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei93b3JrZmxvdy1wcm9qZWN0cy5odG1sKSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCg0KVGhlbiBpZiB3ZSBjcmVhdGUgYSBkaXJlY3Rvcnkgb3IgZm9sZGVyIGNhbGxlZCBgaW1nYCBhbmQgcGxhY2Ugb3VyIGltYWdlIGZpbGVzIGluIHRoaXMgZGlyZWN0b3J5LCB0aGVuIHdlIGNhbiBzcGVjaWZ5IHRoZSBmdWxsIHBhdGggdG8gdGhpcyBmaWxlIG9uIG91ciBjb21wdXRlciwgYnkganVzdCB1c2luZyBgaGVyZTo6aGVyZSgiaW1nIiwgIm5hbWVfb2ZfaW1hZ2UucG5nIilgLiBUaGUgYGluY2x1ZGVfZ3JhcGhpY3MoKWAgZnVuY3Rpb24gd29ya3MgZm9yIGEgdmFyaWV0eSBpbWFnZSBmaWxlIHR5cGVzLiANCg0KYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAibmV3ZGlyLnBuZyIpKQ0KYGBgDQoNCkFsc28geW91IG1heSBoYXZlIG5vdGljZWQgdGhlICBgPHN0eWxlPmAgaHRtbCBjb2RlIHRvIGFkZCBhIGJsdWUgYW5kIGdyZWVuIGJhY2tncm91bmQgdG8gcG9ydGlvbnMgb2YgdGhlIHRleHQuIA0KDQpUaGUgdGV4dCB0aGF0IHdlIHdhbnQgYWx0ZXJlZCB3aXRoIHRoaXMgcGFydGljdWxhciBzdHlsZSBpcyBkZWxpbmVhdGVkIGJ5IHRoZSBgPGRpdj5gIHRvIHN0YXJ0IGFuZCB0aGUgYDwvZGl2PmAgdG8gZW5kIHRoZSBzdHlsZS4gDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBvbmUgdG8gZXhwbGFpbiB3aGF0IGlzIGhhcHBlbmluZyBoZXJlOg0KDQpgYGANCjxzdHlsZT4NCmRpdi5ncmVlbiB7IGJhY2tncm91bmQtY29sb3I6IzhGQkM4RjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4OyBmb250LXNpemU6IDFlbTsgY29sb3I6IHdoaXRlO30NCjwvc3R5bGU+DQo8ZGl2IGNsYXNzID0gImdyZWVuIj4NClJpZWRtYW4sIERhdmlkLCBhbmQgRGVzbW9uZCBP4oCZTmVpbGwuIOKAnENIRFMg4oCTIEstMTIgU2Nob29sIFNob290aW5nIERhdGFiYXNlLuKAnSBDZW50ZXIgZm9yIEhvbWVsYW5kIERlZmVuc2UgYW5kIFNlY3VyaXR5LCBKdW5lIDIwMjAsIFt3d3cuY2hkcy51cy9zc2RiXSh3d3cuY2hkcy51cy9zc2RiKS4NCjwvZGl2Pg0KYGBgDQoNClRoZSBpbnN0cnVjdGlvbnMgZm9yIHRoZSBzdHlsZSBhcmUgd2l0aGluIHRoZSBgPHN0eWxlPmAgYW5kIGA8L3N0eWxlPmAgY29udGVudCBkaXZpZGVycy4gSW5zaWRlIHRoZXNlIGRpdmlkZXJzIGlzIFtDU1NdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUykgY29kZSwgd2hpY2ggaXMgd2hhdCBpcyB1c2VkIHRvIHN0eWxpemUgSFRNTC4gVGhlIGBkaXYuZ3JlZW5gIGlzIHRoZSBuYW1lIG9mIHRoaXMgcGFydGljdWxhciBzdHlsZSB3aGljaCBpbnZvbHZlcyBhIHBhcnRpY3VsYXIgYmFja2dyb3VuZCBjb2xvciAoIzhGQkM4RiAtIHNlZSBbaGVyZV0oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9jc3NyZWYvY3NzX2NvbG9ycy5hc3ApIGZvciBtb3JlIG9wdGlvbnMpLCB3aXRoIGEgYm9hcmRlciByYWRpdXMgb2YgNSBwaXhlbCB0byByb3VuZCB0aGUgZWRnZXMgb2YgdGhlIGJhY2tncm91bmQgY29sb3IgYXJvdW5kIHRoZSB0ZXh0IHdpdGggYSBzaXplIDUgcGl4ZWwgcmFkaXVzLiBUaGUgY29kZSBhbHNvIHN0YXRlcyB0aGF0IGEgW3BhZGRpbmddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9wYWRkaW5nKSBzcGVjaWZpY2F0aW9uIGZvciB0aGUgc2l6ZSBvZiB0aGUgbWFyZ2lucyBvZiB0aGUgdGV4dCBib3ggYXJvdW5kIHRoZSB0ZXh0IGFuZCBpdCBzcGVjaWZpZXMgdGhhdCBmb250IHNob3VsZCBiZSBvZiAxIFtlbSB1bml0c10oaHR0cHM6Ly93d3cudzMub3JnL1N0eWxlL0V4YW1wbGVzLzAwNy91bml0cy5lbi5odG1sKSAod2hpY2ggc3RhbmRzIGZvciBlbGVtZW50IC0gdGh1cyAxIHVuaXQgcmVsYXRpdmUgdG8gdGhlIHNpemUgb2YgdGhlIGVsZW1lbnQpICBhbmQgdGhhdCB0aGUgZm9udCBzaG91bGQgYmUgd2hpdGUuICANCg0KVGhlIGBkaXYuZ3JlZW4gYCBzcGVjaWZpZXMgdGhhdCBgZ3JlZW5gIGlzIHRoZSBuYW1lIG9mIHRoaXMgc3R5bGUsIHRodXMgd2UgY2FuIHRoZW4gdXNlIGA8ZGl2IGNsYXNzID0gZ3JlZW4+YCAoY2FsbGVkIGEgW0NTUyBzZWxlY3Rvcl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9HbG9zc2FyeS9DU1NfU2VsZWN0b3IpKSB0byBzdHlsZSB0aGUgdGV4dCB0aGlzIHdheS4gVGhpcyBjYW4gdGhlbiBiZSB1c2VkIGFnYWluIGFueSB0aW1lIHdlIHdhbnQgdGhpcyBzdHlsZSBsaWtlIHNvOg0KDQpgYGANCjxkaXYgY2xhc3MgPSAiZ3JlZW4iPg0KDQp0ZXh0IA0KDQo8L2Rpdj4NCg0KYGBgDQoNClNlZSB0aGlzIFt3ZWJzaXRlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9IVE1ML0VsZW1lbnQvZGl2KSB0byBsZWFybiBtb3JlIGFib3V0IEhUTUwgYW5kIENTUy4NCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KIyMgKipUaGUgRGF0YSBQYWdlIChJbnRlcmFjdGl2ZSkqKg0KKioqDQoNCkxldCdzIGNyZWF0ZSBhIHBhZ2UgYWJvdXQgdGhlIGRhdGEgdGhhdCB3ZSBhcmUgdXNpbmcuDQoNCiMjIyAqKkxvb2sqKg0KKioqDQoNCg0KVGhpcyBpcyB3aGF0IHRoZSBwYWdlIHdpbGwgbG9vayBsaWtlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAiVGhlX0RhdGFfUGFnZS5wbmciKSkNCmBgYA0KDQojIyMgKipPdmVyYWxsIFN0cnVjdHVyZSoqDQoqKioNCg0KSGVyZSBpcyB0aGUgb3ZlcmFsbCBzdHJ1Y3R1cmUgZm9yIHRoaXMgcGFnZToNCg0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciICwgIkRhdGFfcGFnZV9zdHJ1Y3R1cmUucG5nIikpDQpgYGANCg0KIyMjICoqRGV0YWlscyoqDQoqKioNCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIHRoZSBjb2RlIGZvciB0aGlzIHBhZ2UuIDwvc3VtbWFyeT4NCiANClRvIGNyZWF0ZSB0aGUgc3RydWN0dXJlIGZvciB0aGlzIHBhZ2UgdGhhdCB3aWxsIGRpc3BsYXkgdGhlIGRhdGEsIHdlIGhhdmUgdHdvIGNvbHVtbnMsIHdpdGggdGhlIGZpcnN0IG9uZSAob24gdGhlIGxlZnQpIHdpZGVyIHRoYW4gdGhlIG90aGVyLiBBZ2FpbiB3ZSBoYXZlIGEgYmxvY2sgb2YgdGV4dCBpbiB0aGUgY29sdW1uIG9uIHRoZSBsZWZ0IGxpa2Ugc286DQoNCmBgYA0KVGhlIERhdGEge2RhdGEtaWNvbj0iZmEtZGF0YWJhc2UifQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSANCg0KDQpDb2x1bW4ge2RhdGEtd2lkdGg9NzB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIw0KDQpUaGUgZGF0YSB1c2VkIGluIHRoaXMgZGFzaGJvYXJkIGlzIGZyb20gdGhlIFsqKkNlbnRlciBmb3IgSG9tZWxhbmQgRGVmZW5zZSBhbmQgU2VjdXJpdHkgKENIRFMpKipdKENlbnRlciBmb3IgSG9tZWxhbmQgRGVmZW5zZSBhbmQgU2VjdXJpdHkgKENIRFMpKSBbKipLLTEyIFNob29sIFNob290aW5nIERhdGFiYXNlKipdKGh0dHBzOi8vd3d3LmNoZHMudXMvc3NkYi9hYm91dC8pLiANCg0KVGhlaXIgbWV0aG9kcyBmb3IgaWRlbnRpZnlpbmcgYW5kIGF1dGhlbnRpY2F0aW5nIGluY2lkZW50cyBhcmUgb3V0bGluZWQgW2hlcmVdKGh0dHBzOi8vd3d3LmNoZHMudXMvc3NkYi9tZXRob2RzLykuDQoNCkFjY29yZGluZyB0byB0aGVpciB3ZWJzaXRlOiANCg0KKiJUaGUgZGF0YWJhc2UgY29tcGlsZXMgaW5mb3JtYXRpb24gZnJvbSBtb3JlIHRoYW4gMjUgZGlmZmVyZW50IHNvdXJjZXMgaW5jbHVkaW5nIHBlZXItcmV2aWV3ZWQgc3R1ZGllcywgZ292ZXJubWVudCByZXBvcnRzLCBtYWluc3RyZWFtIG1lZGlhLCBub24tcHJvZml0cywgcHJpdmF0ZSB3ZWJzaXRlcywgYmxvZ3MsIGFuZCBjcm93ZC1zb3VyY2VkIGxpc3RzIHRoYXQgaGF2ZSBiZWVuIGFuYWx5emVkLCBmaWx0ZXJlZCwgZGVjb25mbGljdGVkLCBhbmQgY3Jvc3MtcmVmZXJlbmNlZC4gKipBbGwgb2YgdGhlIGluZm9ybWF0aW9uIGlzIGJhc2VkIG9uIG9wZW4tc291cmNlIGluZm9ybWF0aW9uIGFuZCAzcmQgcGFydHkgcmVwb3J0aW5nLi4uIGFuZCBtYXkgaW5jbHVkZSByZXBvcnRpbmcgZXJyb3JzLioqIioNCg0KKioqDQoNCg0KQ29sdW1uIHtkYXRhLXdpZHRoPTMwfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMNCg0KYGBgDQoNCk5vdyB3ZSB3aWxsIGFkZCBvdXIgYERUX3RhYmxlYCB0byB0aGUgZmlyc3QgY29sdW1uLiBGaXJzdCwgd2UgbmVlZCB0byBpbmNsdWRlIHRoZSBjb2RlIHRoYXQgd2UgcHJldmlvdXNseSB1c2VkIHRvIGNyZWF0ZSB0aGUgYERUX3RhYmxlYCBpbiBvdXIgZGFzaGJvYXJkIGAuUm1kYCBmaWxlOg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KRFRfdGFibGUgPC0gc2hvb3RpbmdfZGF0YSAlPiUNCiAgZHBseXI6OnNlbGVjdChEYXRlLA0KICAgICAgICAgICAgICAgIFNjaG9vbCwNCiAgICAgICAgICAgICAgICBDaXR5LA0KICAgICAgICAgICAgICAgIFN0YXRlLA0KICAgICAgICAgICAgICAgIGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCwNCiAgICAgICAgICAgICAgICBgTmFycmF0aXZlIChEZXRhaWxlZCBTdW1tYXJ5LyBCYWNrZ3JvdW5kKWApICU+JQ0KICByZW5hbWUoIkRlYXRocyIgPSBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApICU+JQ0KICByZW5hbWUoIk5hcnJhdGl2ZSIgPSBgTmFycmF0aXZlIChEZXRhaWxlZCBTdW1tYXJ5LyBCYWNrZ3JvdW5kKWApDQpgYGANCg0KV2UgdGhlbiBpbmNsdWRlIHNvbWUgY29kZSB0byByZW5kZXIgdGhpcyBpbnRlcmFjdGl2ZSB0YWJsZSBpbiBvdXIgZGFzaGJvYXJkLiBTaW5jZSB3ZSBoYXZlIGBzaGlueWAgZW5hYmxlZCBpbiBvdXIgWUFNTCBoZWFkZXIsIHdlIGNhbiB1c2UgdGhlIGByZW5kZXJEYXRhVGFibGUoKWAgb2YgdGhlIGBEVGAgcGFja2FnZSB0byBwcm9kdWNlIHRoZSBvdXRwdXQgd2UgZGVzaXJlLiANCg0KV2UgYWxzbyB3YW50IHRvIHVzZSB0aGUgYG9wdGlvbnNgIGFyZ3VtZW50IHRvIHNwZWNpZnkgaG93IHRoZSBkYXRhIGlzIHJlbmRlcmVkLiBUaGUgYHNjcm9sbGVyID0gVFJVRWAgYXJndW1lbnQgYWRkcyBhIHNjcm9sbCBiYXIgdG8gdGhlIHRhYmxlLCB0aGUgYHNjcm9sbFlgIGFyZ3VtZW50IHNwZWNpZmllcyB0aGF0IHRoZSBzY3JvbGwgYmFyIHNob3VsZCBiZSBmb3IgdGhleSBZIGF4aXMgZGlyZWN0aW9uICh1cCBhbmQgZG93bikgb2YgdGhlIHRhYmxlIGFuZCBzcGVjaWZpZXMgaG93IGxhcmdlIHRoZSBzY3JvbGxlciBzaG91bGQgYmUsIHRoZSBgcGFnZUxlbmd0aGAgYXJndW1lbnQgc3BlY2lmaWVzIGhvdyBtYW55IHJvd3Mgc2hvdWxkIGJlIGRpc3BsYXllZCBzaW11bHRhbmVvdXNseSB3aXRoaW4gdGhlIHRhYmxlLCBhbmQgdGhlIGBhdXRvV2lkdGggPSBUUlVFYCBhcmd1bWVudCBzcGVjaWZpZXMgdGhhdCB0aGUgdGFibGUgc2hvdWxkIGZpdCB0aGUgc3BhY2Ugb2YgdGhlIGNvbHVtbiBvciBwYWdlIGl0IGlzIHdpdGhpbi4gDQoNCldlIHdpbGwgYWxzbyBhZGQgYSBjYXB0aW9uIHdpdGggYSBsaW5rIHRvIHRoZSBvcmlnaW5hbCBkYXRhIHVzaW5nIHRoZSBgdGFncygpYCBhbmQgYHdpdGhUYWdzKClgIGZ1bmN0aW9ucyBvZiB0aGUgYGh0bWx0b29sc2AgcGFja2FnZS4gRGlmZmVyZW50IG9wdGlvbnMgZm9yIHR5cGVzIG9mIHRhZ3MgY2FuIGJlIHNlbGVjdGVkIHVzaW5nIHRoZSBgJGAuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJ0YWdzLnBuZyIpKQ0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KRFQ6OnJlbmRlckRhdGFUYWJsZSh7DQogIERUOjpkYXRhdGFibGUoRFRfdGFibGUsDQogICAgICAgICAgICAgICAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKA0KICAgICAgICAgICAgICAgICAgc3R5bGUgPSAnY2FwdGlvbi1zaWRlOiB0b3A7IHRleHQtYWxpZ246IExlZnQ7JywNCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6d2l0aFRhZ3MoDQogICAgICAgICAgICAgICAgICAgIGRpdihIVE1MKCc8YSBocmVmPSJodHRwczovL3d3dy5jaGRzLnVzL3NzZGIvYWJvdXQvKSI+Q2xpY2sgaGVyZSB0byBiZSByZWRpcmVjdGVkIHRvIGEgcGFnZSB3aGVyZSB0aGlzIGRhdGEgY2FuIGJlIGRvd25sb2FkZWQuPC9hPicpKSkpLA0KICAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoYXV0b1dpZHRoID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSAnNDUwcHgnKSkNCn0pDQpgYGANCg0KV2Ugd2lsbCBhbHNvIGFkZCBhbm90aGVyIGltYWdlIHRvIHRoZSBjb2x1bW4gb24gdGhlIHJpZ2h0LCBvdmVyYWxsIHRoZSBjb2RlIGxvb2tzIGxpa2UgdGhpczoNCg0KYGBgDQoNClRoZSBEYXRhIHtkYXRhLWljb249ImZhLWRhdGFiYXNlIn0NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gDQoNCkNvbHVtbiB7ZGF0YS13aWR0aD03MH0NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjDQoNClRoZSBkYXRhIHVzZWQgaW4gdGhpcyBkYXNoYm9hcmQgaXMgZnJvbSB0aGUgWyoqQ2VudGVyIGZvciBIb21lbGFuZCBEZWZlbnNlIGFuZCBTZWN1cml0eSAoQ0hEUykqKl0oQ2VudGVyIGZvciBIb21lbGFuZCBEZWZlbnNlIGFuZCBTZWN1cml0eSAoQ0hEUykpIFsqKkstMTIgU2hvb2wgU2hvb3RpbmcgRGF0YWJhc2UqKl0oaHR0cHM6Ly93d3cuY2hkcy51cy9zc2RiL2Fib3V0LykuIA0KDQpUaGVpciBtZXRob2RzIGZvciBpZGVudGlmeWluZyBhbmQgYXV0aGVudGljYXRpbmcgaW5jaWRlbnRzIGFyZSBvdXRsaW5lZCBbaGVyZV0oaHR0cHM6Ly93d3cuY2hkcy51cy9zc2RiL21ldGhvZHMvKS4NCg0KUHJldmlvdXNseSwgYWNjb3JkaW5nIHRvIHRoZWlyIHdlYnNpdGU6IA0KDQoqIlRoZSBkYXRhYmFzZSBjb21waWxlcyBpbmZvcm1hdGlvbiBmcm9tIG1vcmUgdGhhbiAyNSBkaWZmZXJlbnQgc291cmNlcyBpbmNsdWRpbmcgcGVlci1yZXZpZXdlZCBzdHVkaWVzLCBnb3Zlcm5tZW50IHJlcG9ydHMsIG1haW5zdHJlYW0gbWVkaWEsIG5vbi1wcm9maXRzLCBwcml2YXRlIHdlYnNpdGVzLCBibG9ncywgYW5kIGNyb3dkLXNvdXJjZWQgbGlzdHMgdGhhdCBoYXZlIGJlZW4gYW5hbHl6ZWQsIGZpbHRlcmVkLCBkZWNvbmZsaWN0ZWQsIGFuZCBjcm9zcy1yZWZlcmVuY2VkLiAqKkFsbCBvZiB0aGUgaW5mb3JtYXRpb24gaXMgYmFzZWQgb24gb3Blbi1zb3VyY2UgaW5mb3JtYXRpb24gYW5kIDNyZCBwYXJ0eSByZXBvcnRpbmcuLi4gYW5kIG1heSBpbmNsdWRlIHJlcG9ydGluZyBlcnJvcnMuKioiKg0KDQoqKioNCg0KJycne3IsIGVjaG89RkFMU0V9DQojIENyZWF0ZSB0aGUgRFQgdGFibGUgZmlyc3QNCkRUX3RhYmxlIDwtIHNob290aW5nX2RhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoRGF0ZSwNCiAgICAgICAgICAgICAgICBTY2hvb2wsDQogICAgICAgICAgICAgICAgQ2l0eSwNCiAgICAgICAgICAgICAgICBTdGF0ZSwNCiAgICAgICAgICAgICAgICBgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAsDQogICAgICAgICAgICAgICAgYE5hcnJhdGl2ZSAoRGV0YWlsZWQgU3VtbWFyeS8gQmFja2dyb3VuZClgKSAlPiUNCiAgcmVuYW1lKCJEZWF0aHMiID0gYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgcmVuYW1lKCJOYXJyYXRpdmUiID0gYE5hcnJhdGl2ZSAoRGV0YWlsZWQgU3VtbWFyeS8gQmFja2dyb3VuZClgKQ0KIyBJbnN0ZWFkIG9mIGRlcGVuZGluZyBvbiB0aGUgc3Rfaml0dGVyIGFsZ29yaXRobSB0byBnZW5lcmF0ZSByYW5kb20gcGxhY2VtZW50LCBhIGN1c3RvbSBmdW5jdGlvbiBwbGFjaW5nIHRoZSBwb2ludHMgc2lkZSBieSBzaWRlIGF0IGEgc2V0IGRpc3RhbmNlIGNvdWxkIGJlIHVzZWQgdG8gbWFrZSBwb2ludHMgb2NjdXJpbmcgYXQgdGhlIHNhbWUgbG9jYXRpb24gYXBwZWFyIG5lYXRseSBhcGFydC4NCicnJw0KDQonJyd7ciwgZWNobz1GQUxTRX0NCkRUOjpyZW5kZXJEYXRhVGFibGUoew0KICBEVDo6ZGF0YXRhYmxlKERUX3RhYmxlLA0KICAgICAgICAgICAgICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigNCiAgICAgICAgICAgICAgICAgIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyB0ZXh0LWFsaWduOiBMZWZ0OycsDQogICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OndpdGhUYWdzKA0KICAgICAgICAgICAgICAgICAgICBkaXYoSFRNTCgnPGEgaHJlZj0iaHR0cHM6Ly93d3cuY2hkcy51cy9zc2RiL2Fib3V0LykiPkNsaWNrIGhlcmUgdG8gYmUgcmVkaXJlY3RlZCB0byBhIHBhZ2Ugd2hlcmUgdGhpcyBkYXRhIGNhbiBiZSBkb3dubG9hZGVkLjwvYT4nKSkpKSwNCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdChhdXRvV2lkdGggPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9ICc0NTBweCcpKQ0KfSkNCicnJw0KDQpDb2x1bW4ge2RhdGEtd2lkdGg9MzB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIw0KDQonJyd7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD0iW1Bob3RvZ3JhcGggYnkgUnViw6luIFJvZHJpZ3Vlel0oaHR0cHM6Ly91bnNwbGFzaC5jb20vcGhvdG9zL0lYVHZuT09TVHlVKSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicnViZW4tcm9kcmlndWV6LUlYVHZuT09TVHlVLXVuc3BsYXNoLmpwZyIpKQ0KJycnDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KIyMgKipUaGUgVVMgU3RhdGlzdGljcyBQYWdlKioNCioqKg0KDQpMZXQncyBjcmVhdGUgYSBwYWdlIGZvciAqKlVTIFN0YXRpc3RpY3MqKiB3ZSB3b3VsZCBsaWtlIHRvIHNoYXJlLiANCg0KIyMjICoqTG9vayoqDQoqKioNClRoaXMgaXMgd2hhdCB0aGUgcGFnZSB3aWxsIGxvb2sgbGlrZToNCg0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciICwgIlVTX1N0YXRpc3RpY3NfcGFnZS5wbmciKSkNCmBgYA0KDQojIyMgKipPdmVyYWxsIFN0cnVjdHVyZSoqDQoqKioNCg0KSGVyZSBpcyB0aGUgb3ZlcmFsbCBzdHJ1Y3R1cmUgZm9yIHRoaXMgcGFnZSB3aGljaCB1c2VzIGEgdGFiIGxheW91dDoNCg0KYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iOTAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAiVVNfc3RhdHNfcGFnZV9vdmVydmlldy5wbmciKSkNCmBgYA0KDQoNCg0KIyMjICoqRGV0YWlscyoqDQoqKioNCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIHRoZSBjb2RlIGZvciB0aGlzIHBhZ2UuIDwvc3VtbWFyeT4NCg0KSGVyZSB3ZSB1c2UgdGhlIGAudGFic2V0YCBhbmQgYC50YWJzZXQtZmFkZWAgb3B0aW9ucyBzcGVjaWZpZWQgZm9yIG91ciBmaXJzdCBjb2x1bW4uIA0KDQpgYGANClVTIFN0YXRpc3RpY3Mge2RhdGEtaWNvbj0iZmEtZmxhZyJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IA0KDQoNCkNvbHVtbiB7ZGF0YS13aWR0aD03MCAudGFic2V0IC50YWJzZXQtZmFkZX0NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmBgYA0KDQpBZnRlciBoYXZpbmcgc3BlY2lmaWVkIHRoZSBgLnRhYnNldGAgYW5kIGAudGFic2V0LWZhZGVgIG9wdGlvbnMsIHdlIGNhbiBjcmVhdGUgbmV3IHRhYnMgaW4gdGhlIHNhbWUgd2F5IHdlIHdvdWxkIGFkZCBlbGVtZW50cyB0byBvdXIgZGFzaGJvYXJkIHdpdGggdGhlIGAjIyNgIHN5bnRheC4gSnVzdCBsaWtlIGluIHRoaXMgZXhhbXBsZToNCg0KYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAidGFiLnBuZyIpKQ0KYGBgDQoNCltbc291cmNlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL2xheW91dHMuaHRtbCldDQoNCg0KTGV0J3MgbWFrZSBhIHRhYiBmb3IgeWVhcmx5IHNjaG9vbCBzaG9vdGluZyBldmVudHMgYW5kIGRlYXRocywgYSB0YWIgZm9yIGN1bXVsYXRpdmUgc2Nob29sIHNob290aW5nIGV2ZW50cyBhbmQgZGVhdGhzLCBhbmQgYSB0YWIgYWJvdXQgdGhlIG51bWJlciBvZiBkZWF0aHMgcGVyIHNjaG9vbCBzaG9vdGluZy4gSW4gZWFjaCB0YWIsIHdlIHdpbGwgaW5jbHVkZSB0aGUgY29kZSBmb3IgdGhlIHBsb3RzIHRoYXQgd2UgaGF2ZSBwcmV2aW91c2x5IGNyZWF0ZWQuIA0KDQpgYGANClVTIFN0YXRpc3RpY3Mge2RhdGEtaWNvbj0iZmEtZmxhZyJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IA0KDQpDb2x1bW4ge2RhdGEtd2lkdGg9NzAwIC50YWJzZXQgLnRhYnNldC1mYWRlfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgWWVhcmx5IERlYXRocyBhbmQgU2hvb3RpbmdzDQoNCicnJ3tyfQ0KDQpzdGFydCA8LSAxOTcwDQplbmQgPC0gMjAyMA0KDQpzaG9vdGluZ3NfcGVyX3llYXI8LSBzaG9vdGluZ19kYXRhICU+JQ0KICAgIGdyb3VwX2J5KERhdGVfeWVhcikgJT4lDQogICAgY291bnQoKSAlPiUNCiAgcmVuYW1lKCJTaG9vdGluZ3MiID0gbikgJT4lDQogICAgdW5ncm91cCgpDQoNCmRlYXRoc19wZXJfeWVhcjwtc2hvb3RpbmdfZGF0YSAlPiUgDQogIGdyb3VwX2J5KERhdGVfeWVhcikgJT4lDQogIHN1bW1hcml6ZShEZWF0aHMgPXN1bShgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApKQ0KDQoNCnBlcl95ZWFyPC1mdWxsX2pvaW4oc2hvb3RpbmdzX3Blcl95ZWFyLCBkZWF0aHNfcGVyX3llYXIpDQpwZXJfeWVhciAlPD4lcGl2b3RfbG9uZ2VyKCBjb2xzID0gKC1EYXRlX3llYXIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJldmVudHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImlkIikNCg0KcGVyX3llYXIlPD4lIA0KICBtdXRhdGUoaWQgPSBmb3JjYXRzOjpmY3RfaW5vcmRlcihpZCkpDQoNCnBlcl95ZWFyICU+JQ0KICAgIGdncGxvdChhZXMoeCA9IERhdGVfeWVhciwgeSA9IGV2ZW50cywgZmlsbCA9aWQpKSArDQogICAgZ2VvbV9jb2woKSsNCiAgICBmYWNldF93cmFwKH5pZCwgc2NhbGVzID0gImZyZWUiLCANCiAgICAgICAgICAgICAgIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIoYyhTaG9vdGluZ3MgPSAiU2hvb3RpbmdzICgjIG9mIGV2ZW50cykiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWF0aHMgPSAiRGVhdGhzICgjIG9mIHBlb3BsZSkiKSksIA0KICAgICAgICAgICAgICAgc3RyaXAucG9zaXRpb24gPSAibGVmdCIpKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKHN0YXJ0LCBlbmQsIGJ5ID0gNSksDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoc3RhcnQtMSwgZW5kKzEpKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMjAsIGJ5ID0gMzApLA0KICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoMCwgMTIwLCBieSA9IDMwKSwNCiAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCAxMjEpKSsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgbGFicyh0aXRsZSA9ICJZZWFybHkgU2hvb3RpbmdzIGFuZCBEZWF0aHMgQXR0cmlidXRhYmxlIHRvIFNjaG9vbCBTaG9vdGluZ3MiLA0KICAgICAgICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgIHggPSAiWWVhciIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJibGFjayIpKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCANCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwNCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpKQ0KDQoNCnRoZW1lX2Rhc2hib2FyZCA8LSBmdW5jdGlvbigpeyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCANCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAjdGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE2KSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBzdHJpcC5wbGFjZW1lbnQgPSAib3V0c2lkZSIsDQogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkNCn0NCicnJw0KDQojIyMgWWVhcmx5IEN1bXVsYXRpdmUgRGVhdGhzIGFuZCBTaG9vdGluZ3MNCg0KJycne3J9DQpzaG9vdGluZ3NfcGVyX3llYXJfY3VtIDwtIA0KICAgIHNob290aW5nc19wZXJfeWVhciAlPiUNCiAgICBtdXRhdGUoU2hvb3RpbmdzID0gY3Vtc3VtKFNob290aW5ncykpDQoNCmRlYXRoc19wZXJfeWVhcl9jdW0gPC0gDQogICAgZGVhdGhzX3Blcl95ZWFyICU+JQ0KICAgIG11dGF0ZShEZWF0aHMgPSBjdW1zdW0oRGVhdGhzKSkNCg0KcGVyX3llYXJfY3VtIDwtIGZ1bGxfam9pbihzaG9vdGluZ3NfcGVyX3llYXJfY3VtLCBkZWF0aHNfcGVyX3llYXJfY3VtKQ0KDQpwZXJfeWVhcl9jdW0gJTw+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKFNob290aW5ncywgRGVhdGhzICksIA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImV2ZW50cyIsIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiaWQiKQ0KDQpwZXJfeWVhcl9jdW0gJTw+JSANCiAgbXV0YXRlKGlkID0gZm9yY2F0czo6ZmN0X2lub3JkZXIoaWQpKQ0KDQpwZXJfeWVhcl9jdW0gJT4lDQogIGdncGxvdChhZXMoeCA9IERhdGVfeWVhciwgeSA9IGV2ZW50cywgZmlsbCA9aWQpKSArDQogICAgZ2VvbV9jb2woKSsNCiAgICBmYWNldF93cmFwKH5pZCwgc2NhbGVzID0gImZyZWUiLCANCiAgICAgICAgICAgICAgIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIoYyhTaG9vdGluZ3MgPSAiU2hvb3RpbmdzIChjdW11bGF0aXZlICMgb2YgZXZlbnRzKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlYXRocyA9ICJEZWF0aHMoY3VtdWxhdGl2ZSAjIG9mIHBlb3BsZSkiKSksIA0KICAgICAgICAgICAgICAgc3RyaXAucG9zaXRpb24gPSAibGVmdCIpKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKHN0YXJ0LCBlbmQsIGJ5ID0gNSksDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoc3RhcnQtMSwgZW5kKzEpKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiYmxhY2siKSkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJDdW11bGF0aXZlIFllYXJseSBTaG9vdGluZ3MgYW5kIERlYXRocyBBdHRyaWJ1dGFibGUgdG9cblNjaG9vbCBTaG9vdGluZ3MiLA0KICAgICAgICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgIHggPSAiWWVhciIpICsNCiAgICB0aGVtZV9kYXNoYm9hcmQoKSANCicnJw0KDQojIyMgRGVhdGhzIFBlciBTaG9vdGluZw0KDQonJyd7cn0NCmRlYXRoc19wZXJjX2V2ZW50IDwtIA0KICAgc2hvb3RpbmdfZGF0YSAlPiUNCiAgIGNvdW50KGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCkgJT4lDQogICByZW5hbWUoIm51bV9ldmVudHMiPSBuKSAlPiUNCiAgIG11dGF0ZShwZXJjZW50ID0gcm91bmQobnVtX2V2ZW50cy9zdW0obnVtX2V2ZW50cykqMTAwLCBkaWdpdHMgPTEpKQ0KDQpncmVhdGVyX3RoYW40IDwtIA0KICBkZWF0aHNfcGVyY19ldmVudCAlPiUgDQogIGZpbHRlcihgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAgPj0gNCkgJT4lIA0KICBjb2xTdW1zKCkNCg0KZGVhdGhzX3BlcmNfZXZlbnQgJTw+JSBiaW5kX3Jvd3MoZ3JlYXRlcl90aGFuNCkNCg0KZGVhdGhzX3BlcmNfZXZlbnQgJTw+JSANCiAgbXV0YXRlKGNhdGVnb3J5ID0gcGFzdGUwKGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCwgIiBkZWF0aHMgIiwgIlxuKCIsIHBlcmNlbnQsICIlKSIpKSANCg0KZGVhdGhzX3BlcmNfZXZlbnQgJTw+JSANCiAgbXV0YXRlKGNhdGVnb3J5ID0gY2FzZV93aGVuKA0KICAgIGNhdGVnb3J5ID09ICBsYXN0KHB1bGwoZGVhdGhzX3BlcmNfZXZlbnQsIGNhdGVnb3J5KSkgfiBwYXN0ZTAoIjQrIGRlYXRocyAiLCAiXG4oIiwgcGVyY2VudCwgIiUpIiksDQogICAgY2F0ZWdvcnkgPT0gIjEgZGVhdGhzIiB+ICIxIGRlYXRoIiwNCiAgICBUUlVFIH4gY2F0ZWdvcnkpKQ0KDQpkZWF0aHNfcGVyY19ldmVudCAlPiUgDQogIHNlbGVjdCgtYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoY2F0ZWdvcnksICIwIGRlYXRoc3wxIGRlYXRofDIgZGVhdGhzfDMgZGVhdGhzfDRcXCsiKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gcm91bmQocGVyY2VudCkpICU+JQ0KICBzZWxlY3QoLW51bV9ldmVudHMpICU+JQ0KICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNhdGVnb3J5LCANCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwZXJjZW50KSAlPiUNCiAgd2FmZmxlOjp3YWZmbGUobGVnZW5kX3BvcyA9ICJib3R0b20iLCB0aXRsZSA9ICJEZWF0aHMgUGVyIFNjaG9vbCBTaG9vdGluZyIsIA0KICAgICAgIHhsYWI9IjEgc3F1YXJlIH4gMSUiKSsgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkNCg0KJycnDQpgYGANCg0KSW4gdGhlIHNlY29uZCBjb2x1bW4sIHdlIHdpbGwgaW5jbHVkZSB3aGF0IGFyZSBjYWxsZWQgdmFsdWUgYm94ZXMgdG8gY29udGFpbiBzdGF0aXN0aWNzIHRoYXQgd2lsbCByZW1haW4gc3RhdGljIGFzIHRoZSB1c2VyIG1vdmVzIHRocm91Z2ggdGhlIHRhYnMgb2YgdGhlIGZpcnN0IGNvbHVtbi4NCg0KYGBgDQpDb2x1bW4ge2RhdGEtd2lkdGg9MzB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIA0KYGBgDQoNCldlIHdhbnQgdG8gZGlzcGxheSBzb21lIGltcG9ydGFudCBzdGF0aXN0aWNzLCBzdWNoIGFzOg0KDQorIFRvdGFsIG51bWJlciBvZiBwZW9wbGUgd291bmRlZCBpbiBhIHNjaG9vbCBzaG9vdGluZyAgIA0KKyBUb3RhbCBudW1iZXIgb2YgZGVhdGhzIGZyb20gYSBzY2hvb2wgc2hvb3RpbmcgIA0KKyBNZWRpYW4gbnVtYmVyIG9mIHNob3RzIGZpcmVkICANCisgUGVyY2VudGFnZSBvZiBzY2hvb2wgc2hvb3RpbmdzIHdoZXJlIHRoZSBzaG9vdGVyIHdhcyB0aGUgb25seSB2aWN0aW0gIA0KKyBQZXJjZW50YWdlIG9mIHNjaG9vbCBzaG9vdGluZ3Mgd2hlcmUgYSBzaW5nbGUgaGFuZGd1biB3YXMgdXNlZCAgDQorIFBlcmNlbnRhZ2Ugb2Ygc2Nob29sIHNob290aW5ncyB3aGVyZSB0aGUgc2hvb3RlciB3YXMgbWFsZQ0KDQpUbyBjcmVhdGUgYSB2YWx1ZSBib3ggd2Ugd2lsbCB1c2UgdGhlIGB2YWx1ZUJveCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4gVGhlIHRleHQgZm9yIHRoZSB0aGUgdmFsdWUgYm94IGlzIHNwZWNpZmllZCBieSB0aGUgdGV4dCBmb2xsb3dpbmcgdGhlIGAjIyNgIHN5bnRheC4NCg0KVGhlcmUgYXJlIGEgZmV3IGFyZ3VtZW50cyB0byBiZSBhd2FyZSBvZiBmb3IgdGhpcyBmdW5jdGlvbjoNCg0KMS4gYHZhbHVlYCAtIHRoaXMgaXMgdGhlIHZhbHVlIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgYm94IC0gdGhpcyB1c3VhbGx5IGEgbnVtYmVyLCBidXQgbWlnaHQgYmUgdGV4dA0KMi4gYGNhcHRpb25gIC0gaWYgZGVzaXJlZCwgeW91IGNhbiBhZGQgdGV4dCB0byBiZSBkaXNwbGF5ZWQgdW5kZXIgdGhlIHZhbHVlIGJ1dCBrZWVwIGluIG1pbmQgdGhhdCB5b3Ugd2lsbCBhbHNvIGluY2x1ZGUgdGV4dCB3aXRoIHRoZSBgIyMjYCBzeW50YXgNCjMuIGBpY29uYCAtIGlmIHlvdSB3b3VsZCBsaWtlIHRvIGFkZCBhbiBpY29uIHlvdSBjYW4gc3BlY2lmeSBpdCBsaWtlIHNvOiBgaWNvbiA9IGZhLWZsYWdgDQo0LiBgY29sb3JgIC0gdGhpcyBjaGFuZ2VzIHRoZSBjb2xvciBvZiB0aGUgYm94DQo1LiBgaHJlZmAgLSBpZiB5b3Ugd291bGQgbGlrZSB0byBhZGQgYSBVUkwgbGluayB5b3UgY2FuIGRvIHNvIHdpdGggdGhpcyBhcmd1bWVudA0KDQpXZSBjYW4gY3JlYXRlIGEgdmFsdWUgYm94IGZvciB0aGUgdG90YWwgbnVtYmVyIG9mIHBlb3BsZSB3b3VuZGVkIGFzIGZvbGxvd3MsIHdoZXJlIHdlIHVzZSB0aGUgYmFzZSBgc3VtKClgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgc3VtIG9mIGFsbCB0aGUgdmFsdWVzIGZvciB0aGUgYFdvdW5kZWRgIHZhcmlhYmxlIHdoaWNoIHdhcyBleHRyYWN0ZWQgdXNpbmcgdGhlIGBwdWxsKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZS4gV2UgbmVlZCB0byByZW1vdmUgYE5BYCB2YWx1ZXMgdG8gYmUgYWJsZSB0byBjYWxjdWxhdGUgdGhlIHN1bSBhbmQgd2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBuYS5ybSA9IFRSVUVgIGFyZ3VtZW50LiANCg0KDQpgYGANCkNvbHVtbiB7ZGF0YS13aWR0aD0zMDB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIA0KDQoNCiMjIyAqKlRvdGFsIFdvdW5kZWQqKg0KICAgIA0KJycne3J9DQp2YWx1ZUJveCh2YWx1ZSA9IHN1bShwdWxsKHNob290aW5nX2RhdGEsIFdvdW5kZWQpLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgY29sb3IgPSAid2hpdGUiKQ0KJycnDQogICAgDQojIyMgKipUb3RhbCBEZWF0aHMqKg0KDQonJyd7cn0NCnZhbHVlQm94KHZhbHVlID0gc3VtKHB1bGwoDQogIHNob290aW5nX2RhdGEsYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIGNvbG9yID0gIndoaXRlIikNCicnJw0KDQpgYGANCg0KVG8gY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHNjaG9vbCBzaG9vdGluZ3Mgd2hlcmUgdGhlIHNob290ZXIgY29tbWl0dGVkIG9yIGF0dGVtcHRlZCBzdWljaWRlLCB3ZSB3aWxsIHVzZSBvdXIgY2FsY3VsYXRpb24gd2hpY2ggd2FzIGV4cGxhaW5lZCBpbiB0aGUgW0RhdGEgQW5hbHlzaXMgYW5kIFZpc3VhbGl6YXRpb25dIHNlY3Rpb24uIFRoZSBgcGFzdGUwYCBmdW5jdGlvbiBpcyB1c2VkIHRvIGFkZCB0aGUgcGVyY2VudGFnZSBzeW1ib2wuDQoNCmBgYA0KDQojIyMgKipTaG9vdGVyIGNvbW1pdHRlZCBvciBhdHRlbXB0ZWQgc3VpY2lkZSoqDQoNCicnJ3tyfQ0KDQpzdWljaWRlIDwtIChzdW0ocHVsbChzaG9vdGluZ19kYXRhLGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApLCBuYS5ybSA9IFRSVUUpIC8NCiAgICAgICAgICAgIHN1bShwdWxsKHNob290aW5nX2RhdGEsIGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApPj0wLCBuYS5ybSA9IFRSVUUpKSoxMDANCnN1aWNpZGUgPC0gcm91bmQoc3VpY2lkZSwgMSkNCg0KcmVwb3J0aW5nX3N1aWMgPC0gKHN1bShwdWxsKHNob290aW5nX2RhdGEsIGBTdWljaWRlIChvciBhdHRlbXB0ZWQgc3VpY2lkZSkgYnkgU2hvb3RlciAoWS9OKWApPj0wLCBuYS5ybSA9IFRSVUUpLw0KICAgICAgICAgICAgICBsZW5ndGgocHVsbChzaG9vdGluZ19kYXRhLCBgU3VpY2lkZSAob3IgYXR0ZW1wdGVkIHN1aWNpZGUpIGJ5IFNob290ZXIgKFkvTilgKSkpKjEwMA0KcmVwb3J0aW5nX3N1aWMgPC0gcm91bmQocmVwb3J0aW5nX3N1aWMsIDEpDQoNCnZhbHVlQm94KHZhbHVlID0gcGFzdGUwKHN1aWNpZGUsIiUiKSwgDQogICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpDQonJycNCg0KYGBgDQoNCkZvciB0aGUgdmFsdWUgYm94IG9mIHRoZSBwZXJjZW50YWdlIG9mIHNjaG9vbCBzaG9vdGluZ3Mgd2hlcmUgYSBzaW5nbGUgaGFuZGd1biB3YXMgdXNlZCB3YXMgY2FsY3VsYXRlZCBieSB1c2luZyB0aGUgYGNhc2Vfd2hlbigpYCBmdW5jdGlvbiB0byBzcGVjaWZ5IGFsbCBjYXNlcyB3aGVyZSB0aGUgYEZpcmVhcm0gVHlwZWAgdmFyaWFibGUgd2FzIGVxdWFsIHRvIGAiSGFuZGd1biJgIGFzIGBUUlVFYCBhbmQgYWxsIG90aGVycyBhcyBgRkFMU0VgLiBUaGlzIGFsbG93cyB1cyB0byB1c2UgdGhlIGJhc2UgYHN1bSgpYCBmdW5jdGlvbiBhcyBgVFJVRWAgdmFsdWVzIHdpbGwgYmUgY291bnRlZCBhcyBhIHZhbHVlIG9mIGAxYCBhbmQgYEZBTFNFYCB2YWx1ZXMgd2lsbCBiZSBjb3VudGVkIGFzIGEgdmFsdWUgb2YgYDBgLiBUaGlzIHN1bSB3YXMgdGhlbiBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2Ygc2Nob29sIHNob290aW5nIGV2ZW50cyBieSBnZXR0aW5nIHRoZSBsZW5ndGggb2YgdGhlIGBGaXJlYXJtIFR5cGVgIHZhcmlhYmxlIHVzaW5nIHRoZSBiYXNlIGBsZW5ndGgoKWAgZnVuY3Rpb24uIFRoZSBuZXh0IHZhbHVlIGJveCBhYm91dCB0aGUgZ2VuZGVyIG9mIHRoZSBzaG9vdGVyIHdhcyBjYWxjdWxhdGVkIGluIGEgc2ltaWxhciBtYW5uZXIuIA0KDQpgYGANCiAgICANCiMjIyAqKlVzZSBvZiBhIFNpbmdsZSBIYW5kZ3VuKioNCg0KJycne3J9DQoNCmhhbmRndW4gPC1wYXN0ZShhcy5jaGFyYWN0ZXIocm91bmQoMTAwICooc3VtKGNhc2Vfd2hlbigNCiAgICAgIHB1bGwoc2hvb3RpbmdfZGF0YSxgRmlyZWFybSBUeXBlYCkgPT0gIkhhbmRndW4iIH4gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpLCBuYS5ybSA9IFRSVUUpDQogICAgLw0KICAgICAgc3VtKHB1bGwoc2hvb3RpbmdfZGF0YSwgYEZpcmVhcm0gVHlwZWApPj0wLCBuYS5ybSA9IFRSVUUpKSwNCiAgICAxKSksICIlIikNCg0KcmVwb3J0aW5nX2d1biA8LSAoc3VtKHB1bGwoc2hvb3RpbmdfZGF0YSwgYEZpcmVhcm0gVHlwZWApPj0wLCBuYS5ybSA9IFRSVUUpLw0KICAgICAgICAgICAgICBsZW5ndGgocHVsbChzaG9vdGluZ19kYXRhLCBgRmlyZWFybSBUeXBlYCkpKSoxMDANCnJlcG9ydGluZ19ndW4gPC0gcm91bmQocmVwb3J0aW5nX2d1biwgMSkNCg0KDQp2YWx1ZUJveCh2YWx1ZSA9IGhhbmRndW4sDQogIGNvbG9yID0gIndoaXRlIikNCg0KJycnDQoNCiMjIyAqKlNob290ZXIgV2FzIE1hbGUqKg0KJycne3J9DQoNCg0KZ2VuZGVyIDwtIHBhc3RlKGFzLmNoYXJhY3Rlcihyb3VuZCgxMDAgKiAoc3VtKA0KICAgIGNhc2Vfd2hlbihwdWxsKHNob290aW5nX2RhdGEsYFNob290ZXIgR2VuZGVyYCkgPT0gIk1hbGUiIH4gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKQ0KICAgIC8NCiAgICAgIHN1bShwdWxsKHNob290aW5nX2RhdGEsIGBTaG9vdGVyIEdlbmRlcmApPj0wLCBuYS5ybSA9IFRSVUUpKSwNCiAgICAxKSksICIlIikNCg0KcmVwb3J0aW5nX21hbGUgPC0gKHN1bShwdWxsKHNob290aW5nX2RhdGEsIGBTaG9vdGVyIEdlbmRlcmApPj0wLCBuYS5ybSA9IFRSVUUpLw0KICAgICAgICAgICAgICBsZW5ndGgocHVsbChzaG9vdGluZ19kYXRhLCBgU2hvb3RlciBHZW5kZXJgKSkpKjEwMA0KcmVwb3J0aW5nX21hbGUgPC0gcm91bmQocmVwb3J0aW5nX21hbGUsIDEpDQoNCg0KdmFsdWVCb3godmFsdWUgPSBwYXN0ZShnZW5kZXIpLA0KICBjb2xvciA9ICJ3aGl0ZSIpDQonJycNCmBgYA0KDQpBZGRpdGlvbmFsIHRleHQgYWJvdXQgdGhlIHJlcG9ydGluZyByYXRlIGZvciB0aGVzZSBzdGF0aXN0aWNzIHdhcyBhZGRlZCB1c2luZyB0aGUgYCMjI2Agc3ludGF4LiBBZGRpdGlvbmFsbHkgaW5saW5lIGNvZGUgaXMgZXZhbHVhdGVkIHVzaW5nIHRoZSBub3RhdGlvbiBgImByIGAiYCBBZ2FpbiBub3RpY2UgdGhhdCBgIiciYCB3YXMgdXNlZCBpbnN0ZWFkIG9mIGAiYCJgIGp1c3QgZm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3NlcyB0byBhbGxvdyB0aGlzIFIgTWFya2Rvd24gZG9jdW1lbnQgdG8gcmVuZGVyIHRoZSBjb2RlIGZyb20gdGhlIGRhc2hib2FyZCBmaWxlLg0KDQpgYGANCg0KIyMjDQoNCnJlcG9ydGluZyByYXRlIG9mIHNob290ZXIgc3VpY2lkZSA9ICdyIHJlcG9ydGluZ19zdWljJyUsICANCnJlcG9ydGluZyByYXRlIG9mIGd1biB0eXBlID0gJ3IgcmVwb3J0aW5nX2d1biclLCAgDQpyZXBvcnRpbmcgcmF0ZSBvZiBzaG9vdGVyIGdlbmRlciA9ICdyIHJlcG9ydGluZ19tYWxlJyUNCg0KYGBgDQoNCjwvZGV0YWlscz4gDQoNCioqKg0KDQojIyAqKlRoZSBTdGF0ZSBTdGF0aXN0aWNzIFBhZ2UgKEludGVyYWN0aXZlKSoqDQoqKioNCg0KTGV0J3MgY3JlYXRlIGEgcGFnZSBmb3IgKipTdGF0ZSBTdGF0aXN0aWNzKiogd2Ugd291bGQgbGlrZSB0byBzaGFyZS4gSW1wb3J0YW50bHkgdGhpcyBwYWdlIGFsbG93cyBmb3IgdGhlIHVzZXIgdG8gY2hvb3NlIHdoYXQgc3RhdGUgdG8gbG9vayBhdC4NCg0KIyMjICoqTG9vayoqDQoqKioNCg0KVGhpcyBpcyB3aGF0IHRoZSBwYWdlIHdpbGwgbG9vayBsaWtlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAiU3RhdGVfU3RhdGlzdGljc19wYWdlLnBuZyIpKQ0KYGBgDQoNCiMjIyAqKk92ZXJhbGwgU3RydWN0dXJlKioNCioqKg0KDQoNCkhlcmUgaXMgdGhlIG92ZXJhbGwgc3RydWN0dXJlIGZvciB0aGlzIHBhZ2U6DQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjkwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciICwgInN0YXRlc19zdGF0c19wYWdlX292ZXJ2aWV3LnBuZyIpKQ0KYGBgDQoNCioqTm90ZSoqOiB0aGUgb3RoZXIgdmFsdWUgQm94ZXMgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGlzIGltYWdlLiBZb3UgY2FuIHNlZSB0aGF0IHRoZSBgcmVuZGVyUGxvdCgpYCBmdW5jdGlvbiBpcyB1c2VkIGZvciBwbG90cyBhbmQgdGhlIGByZW5kZXJWYWx1ZUJveCgpYCBmdW5jdGlvbiBpcyB1c2VkIGZvciB2YWx1ZSBib3hlcy4gDQoNCiMjIyAqKkRldGFpbHMqKg0KKioqDQoNCk9uIHRoaXMgcGFnZSB3ZSB3YW50IHRoZSB1c2VyIHRvIGJlIGFibGUgdG8gc2VsZWN0IGRhdGEgZm9yIGEgc3BlY2lmaWMgc3RhdGUgYW5kIHJlbmRlciBwbG90cyBhbmQgZ2V0IHN0YXRpc3RpY3MganVzdCBmb3IgdGhlIHNlbGVjdGVkIHN0YXRlLiBUbyBkbyB0aGlzIHdlIHdpbGwgdXRpbGl6ZSB0aGUgYHJlbmRlclBsb3QoKWAgYW5kIGByZW5kZXJWYWx1ZUJveCgpYCBmdW5jdGlvbnMgb2YgdGhlIGBmbGV4ZGFzaGJvYXJkYCBwYWNrYWdlLCBhcyB3ZWxsIGFzIHRoZSBgc2VsZWN0SW5wdXQoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHNoaW55YCBwYWNrYWdlLiBTZWUgdGhpcyBbd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZmxleGRhc2hib2FyZC9zaGlueS5odG1sKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB1c2luZyBgc2hpbnlgIHRvIGNyZWF0ZSBpbnRlcmFjdGl2ZSBkYXNoYm9hcmRzIHdpdGggYGZsZXhkYXNoYm9hcmRgLg0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgdGhlIGNvZGUgZm9yIHRoaXMgcGFnZS4gPC9zdW1tYXJ5Pg0KDQpUaGUgZmlyc3QgdGhpbmcgd2UgbmVlZCB0byBkbyB0byBhbGxvdyB0aGlzIHBhZ2UgdG8gYmUgaW50ZXJhY3RpdmUgaXMgdG8gYWRkIGBydW50aW1lOiBzaGlueWAgdG8gdGhlIFlBTUwgaGVhZGVyIGF0IHRoZSB0b3Agb2YgdGhlIFIgTWFya2Rvd24gZmlsZS4NCg0KVGhlIG5leHQgdGhpbmcgd2Ugd2FudCB0byBkbyBpcyBhZGQgdGhlIGB7LnNpZGViYXJ9YCBhdHRyaWJ1dGUgdG8gdGhlIGZpcnN0IGNvbHVtbiBvZiB0aGlzIHBhZ2UuIFRoaXMgYWxsb3dzIHVzIHRvIHVzZSBgc2hpbnlgIGlucHV0IGZ1bmN0aW9ucyBpbiB0aGlzIGNvbHVtbi4NCg0KVGhlbiwgd2UgdXNlIHRoZSBgc2VsZWN0SW5wdXQoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgbWVudSBmb3IgdGhlIHVzZXIgdG8gaW50ZXJhY3Qgd2l0aCBhbmQgYWRkIGl0IHRvIHRoaXMgY29sdW1uLg0KDQpGaW5hbGx5LCB3ZSB1c2UgdGhlIGByZW5kZXJQbG90KClgIGZ1bmN0aW9uICBhbmQgYHJlbmRlclZhbHVlQm94KClgIGZ1bmN0aW9uIHRvIHVzZSB0aGUgaW5wdXQgZnJvbSB0aGUgdXNlciB0byByZW5kZXIgcGxvdHMgYW5kIHZhbHVlIGJveGVzIGJhc2VkIG9uIHRoZWlyIGlucHV0Lg0KDQpUaGUgYHNlbGVjdElucHV0KClgIGZ1bmN0aW9uIGFsbG93cyB1cyB0byBwcm92aWRlIHRoZSB1c2VyIHdpdGggYSBwdWxsIGRvd24gbWVudSBvZiBvcHRpb25zIGZvciBzdGF0ZXMuIFRoZSBtYWluIGFyZ3VtZW50cyBmb3IgdGhpcyBmdW5jdGlvbiBhcmU6DQoNCjEuIGBpbnB1dElkYCAtIHRoaXMgaXMgd2hhdCB0aGUgc2VsZWN0aW9uIHdpbGwgYmUgY2FsbGVkIGluIHN1YnNlcXVlbnQgY29kZQ0KMi4gYGxhYmVsYCAtIHRoaXMgaXMgd2hhdCB0aGUgdXNlciBzZWVzIGFib3ZlIHRoZSBwdWxsIGRvd24gbWVudQ0KMy4gYGNob2ljZXNgIC0gdGhpcyBpcyBhIGxpc3Qgb2Ygb3B0aW9ucyBmb3IgdGhlIG1lbnUNCjQuIGBzZWxlY3RlZGAgLSB0aGlzIGNhdXNlcyBhIHBhcnRpY3VsYXIgb3B0aW9uIHRvIGJlIHRoZSBkZWZhdWx0IGNob2ljZQ0KDQpUaGlzIGlzIHBsYWNlZCBpbiBhIGNvbHVtbiBvbiB0aGUgZmFyIGxlZnQgc2lkZSB0aGF0IGlzIG1vcmUgbmFycm93IHRoYW4gdGhlIG90aGVycy4gDQoNCmBgYA0KU3RhdGUgU3RhdGlzdGljcyB7ZGF0YS1pY29uPWZhLWZsYWctY2hlY2tlcmVkfQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSANCg0KQ29sdW1uIHsuc2lkZWJhciBkYXRhLXdpZHRoPTI1MH0NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk5vdGUgdGhhdCB0aGUgc3RhdGlzdGljcyBzaG93biBkbyBub3QgYWNjb3VudCBmb3Igb3RoZXIgcG9zc2libHkgaW5mbHVlbnRpYWwgc3RhdGUgc3BlY2lmaWMgZmVhdHVyZXMgbGlrZSBwb3B1bGF0aW9uIGRlbnNpdHkgb3IgZ3VuIGxhd3MgYW1vbmcgb3RoZXJzLg0KDQoNCicnJ3tyfQ0KICANCnNlbGVjdElucHV0KGlucHV0SWQgPSAic3RhdGVfc2VsZWN0ZWQiLCANCiAgICAgICAgICAgIGxhYmVsID0gIlNlbGVjdCBhIHN0YXRlIHRvIGV4cGxvcmU6IiwNCiAgICAgICAgICAgIGNob2ljZXMgPSBzaG9vdGluZ19kYXRhICU+JSANCiAgICAgICAgICAgIHB1bGwoU3RhdGUpICU+JSANCiAgICAgICAgICAgIHVuaXF1ZSgpICU+JQ0KICAgICAgICAgICAgc29ydCgpLCBzZWxlY3RlZCA9ICJBbGFiYW1hIikNCg0KIyAgV2FzaGluZ3RvbiwgRC5DLiBnZXRzIGV4Y2x1ZGVkIGJ5IHRoaXMNCicnJw0KYGBgDQoNCk5vdGUgdGhhdCB3ZSB1c2VkIHRoZSBgdW5pcXVlKClgIGZ1bmN0aW9uIHRvIHNlbGVjdCBvbmx5IHVuaXF1ZSB2YWx1ZXMgb2YgdGhlIGBTdGF0ZWAgdmFyaWFibGUgb2YgdGhlIGBzaG9vdGluZ19kYXRhYCB0aWJibGUuIFRoZSBgc29ydCgpYCBmdW5jdGlvbiB3YXMgdXNlZCB0byBwdXQgdGhlIG9wdGlvbnMgaW4gYWxwaGFiZXRpY2FsIG9yZGVyLg0KDQpJbiB0aGUgbmV4dCBjb2x1bW4sIHdlIGhhdmUgb3VyIHBsb3RzIGxpa2Ugd2UgZGlkIG9uIHRoZSBsYXN0IHBhZ2UuIEFnYWluIHdlIHdpbGwgdXNlIGB0YWJzZXRgLiBIb3dldmVyLCB0aGUgZGlmZmVyZW5jZSBoZXJlIGlzIHRoYXQgd2UgbmVlZCB0byBpbmNsdWRlIHRoZSBgcmVuZGVyUGxvdCgpYCBmdW5jdGlvbiBhcm91bmQgYWxsIG9mIG91ciBjb2RlIGZvciBlYWNoIHBsb3QgYW5kIHdlIG5lZWQgdG8gdXNlIHRoZSBkYXRhIHRoYXQgdGhlIHVzZXIgc2VsZWN0ZWQuIA0KDQpUaGlzIHdpbGwgYXV0b21hdGljYWxseSBiZSBpbiBhIGRhdGEgb2JqZWN0IGNhbGxlZCBgaW5wdXRgIGFuZCBpdCB3aWxsIGJlIHdpdGhpbiBhIHZhcmlhYmxlIGNhbGxlZCBgc3RhdGVfc2VsZWN0ZWQiYCBiYXNlZCBvbiB3aGF0IHdlIHVzZWQgZm9yIHRoZSBgaW5wdXRJRGAgaW4gdGhlIGBzZWxlY3RfSW5wdXQoKWAgZnVuY3Rpb24gKHRoaXMgcmVxdWlyZXMgdGhlIGJhc2UgUiB3YXkgb2Ygc2VsZWN0aW5nIGEgc3BlY2lmaWMgdmFyaWFibGUgdXNpbmcgdGhlIGAkYCkuIA0KDQpOb3RpY2UgdGhhdCB0aGUgYHJlbmRlclBsb3QoKWAgZnVuY3Rpb24gcmVxdWlyZXMgdGhhdCB0aGUgY29kZSBiZSB3aXRoaW4gYnJhY2tldHMgYHt9YC4gVGhlIGRhdGEgaXMgZmlsdGVyZWQgZmlyc3QgZm9yIGp1c3QgdGhlIHN0YXRlIHRoYXQgd2FzIHNlbGVjdGVkLiBUaGUgY29kZSBmb3IgdGhlIHBsb3RzIGlzIGVzc2VudGlhbGx5IHRoZSBzYW1lIHdpdGggbWlub3IgbW9kaWZpY2F0aW9ucyB0byBhbGxvdyBmb3IgYWxsIHVuaXF1ZSBjYXNlcyB0aGF0IHRoZSBkaWZmZXJlbnQgc3RhdGVzIHByZXNlbnQuIEZvciBleGFtcGxlIHRoZSBgZGVhdGhzX3BlcmNfZXZlbnQgJTw+JWZpbHRlciAoIWR1cGxpY2F0ZWQoY2F0ZWdvcnkpKWAgaXMgYWRkZWQgdG8gdGhlIGxhc3QgcGxvdCBhYm91dCB0aGUgbnVtYmVyIG9mIGRlYXRocyBwZXIgc2Nob29sIHNob290aW5nIHRvIGF2b2lkIGR1cGxpY2F0aW9uIG9mIHRoZSByb3dzIGluIGNhc2VzIGxpa2UgQ29sb3JhZG8gd2hlcmUgdGhlIHRoZXJlIGlzIG9ubHkgb25lIGV2ZW50IHRoYXQgaGFkIDQgb3IgbW9yZSBkZWF0aHMgKGJlY2F1c2UgaW4gdGhlIG90aGVyIGNhc2VzIHRoaXMgdmFsdWUgaXMgYSBzdW0gb2YgYWxsIHNjaG9vbCBzaG9vdGluZyB3aXRoIDQgb3IgbW9yZSBkZWF0aHMpLiANCg0KSXQncyBhbHdheXMgZ29vZCB0byBjaGVjayBhcyBtYW55IHBvc3NpYmxlIGlucHV0IHZhbHVlcyBhcyBwb3NzaWJsZSB0byBtYWtlIHN1cmUgdGhhdCB5b3VyIHBsb3Qgc2hvd3MgdXAgYXMgeW91IGV4cGVjdCENCg0KYGBgDQpDb2x1bW4ge2RhdGEtd2lkdGg9NzUwIC50YWJzZXQgLnRhYnNldC1mYWRlfQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFllYXJseSBEZWF0aHMgYW5kIFNob290aW5ncw0KDQonJyd7cn0NCnJlbmRlclBsb3Qoew0Kc2hvb3RpbmdfZGF0YV9zdGF0ZSA8LSBzaG9vdGluZ19kYXRhICU+JSBmaWx0ZXIoU3RhdGUgPT0gaW5wdXQkc3RhdGVfc2VsZWN0ZWQpDQoNCnNob290aW5nc19wZXJfeWVhcjwtIHNob290aW5nX2RhdGFfc3RhdGUgICU+JQ0KICAgIGdyb3VwX2J5KERhdGVfeWVhcikgJT4lDQogICAgY291bnQoKSAlPiUNCiAgcmVuYW1lKCJTaG9vdGluZ3MiID0gbikgJT4lDQogICAgdW5ncm91cCgpDQoNCmRlYXRoc19wZXJfeWVhcjwtc2hvb3RpbmdfZGF0YV9zdGF0ZSAgJT4lIA0KICBncm91cF9ieShEYXRlX3llYXIpICU+JQ0KICBzdW1tYXJpemUoRGVhdGhzID1zdW0oYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSkNCg0KDQpwZXJfeWVhciA8LSBmdWxsX2pvaW4oc2hvb3RpbmdzX3Blcl95ZWFyLCBkZWF0aHNfcGVyX3llYXIpDQpwZXJfeWVhciAlPD4lIHBpdm90X2xvbmdlcihjb2xzID0gKC1EYXRlX3llYXIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJldmVudHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImlkIikNCg0KcGVyX3llYXIgJTw+JSANCiAgbXV0YXRlKGlkID0gZm9yY2F0czo6ZmN0X2lub3JkZXIoaWQpKQ0KDQpwZXJfeWVhciAlPD4lDQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZV95ZWFyLCB5ID0gZXZlbnRzLCBmaWxsID1pZCkpICsNCiAgICBnZW9tX2NvbCgpKw0KICAgIGZhY2V0X3dyYXAofmlkLCBzY2FsZXMgPSAiZnJlZSIsIA0KICAgICAgICAgICAgICAgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKFNob290aW5ncyA9ICJTaG9vdGluZ3MgKCMgb2YgZXZlbnRzKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlYXRocyA9ICJEZWF0aHMgKCMgb2YgcGVvcGxlKSIpKSwgDQogICAgICAgICAgICAgICBzdHJpcC5wb3NpdGlvbiA9ICJsZWZ0IikrDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoc3RhcnQsIGVuZCwgYnkgPSA1KSwNCiAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhzdGFydC0xLCBlbmQrMSkpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJibGFjayIpKSsNCiAgICBsYWJzKHRpdGxlID0gIlllYXJseSBTaG9vdGluZ3MgYW5kIERlYXRocyBBdHRyaWJ1dGFibGUgdG8gU2Nob29sIFNob290aW5ncyIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJVbml0ZWQgU3RhdGVzIiwNCiAgICAgICAgIHkgPSBOVUxMLA0KICAgICAgICAgeCA9ICJZZWFyIikgKw0KICAgIHRoZW1lX2Rhc2hib2FyZCgpKw0KICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpDQp9KQ0KJycnDQoNCiMjIyBZZWFybHkgQ3VtdWxhdGl2ZSBEZWF0aHMgYW5kIFNob290aW5ncw0KDQonJyd7cn0NCg0KcmVuZGVyUGxvdCh7DQoNCnNob290aW5nX2RhdGFfc3RhdGUgPC0gc2hvb3RpbmdfZGF0YSAlPiUgZmlsdGVyKFN0YXRlID09IGlucHV0JHN0YXRlX3NlbGVjdGVkKQ0KDQpzaG9vdGluZ3NfcGVyX3llYXI8LSBzaG9vdGluZ19kYXRhX3N0YXRlICAlPiUNCiAgICBncm91cF9ieShEYXRlX3llYXIpICU+JQ0KICAgIGNvdW50KCkgJT4lDQogIHJlbmFtZSgiU2hvb3RpbmdzIiA9IG4pICU+JQ0KICAgIHVuZ3JvdXAoKQ0KDQpzaG9vdGluZ3NfcGVyX3llYXJfY3VtIDwtIA0KICBzaG9vdGluZ3NfcGVyX3llYXIgJT4lDQogIG11dGF0ZShTaG9vdGluZ3MgPSBjdW1zdW0oU2hvb3RpbmdzKSkNCg0KZGVhdGhzX3Blcl95ZWFyPC1zaG9vdGluZ19kYXRhX3N0YXRlICAlPiUgDQogIGdyb3VwX2J5KERhdGVfeWVhcikgJT4lDQogIHN1bW1hcml6ZShEZWF0aHMgPXN1bShgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWApKQ0KDQpkZWF0aHNfcGVyX3llYXJfY3VtIDwtIA0KICBkZWF0aHNfcGVyX3llYXIgJT4lDQogIG11dGF0ZShEZWF0aHMgPSBjdW1zdW0oRGVhdGhzKSkNCg0KcGVyX3llYXJfY3VtIDwtIGZ1bGxfam9pbihzaG9vdGluZ3NfcGVyX3llYXJfY3VtLCBkZWF0aHNfcGVyX3llYXJfY3VtKQ0KDQoNCnBlcl95ZWFyX2N1bSAlPD4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoU2hvb3RpbmdzLCBEZWF0aHMgKSwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiZXZlbnRzIiwgDQogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiaWQiKQ0KICAgICAgICAgICAgICAgIA0KcGVyX3llYXJfY3VtICU8PiUgDQogIG11dGF0ZShpZCA9IGZvcmNhdHM6OmZjdF9pbm9yZGVyKGlkKSkNCg0KcGVyX3llYXJfY3VtICU+JQ0KZ2dwbG90KGFlcyh4ID0gRGF0ZV95ZWFyLCB5ID0gZXZlbnRzLCBmaWxsID1pZCkpICsNCiAgICBnZW9tX2NvbCgpKw0KICAgIGZhY2V0X2dyaWQofmlkKSsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKHN0YXJ0LCBlbmQsIGJ5ID0gNSksDQogICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcShzdGFydCwgZW5kLCBieSA9IDUpLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKHN0YXJ0LTEsIGVuZCsxKSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsYWNrIikpKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZSA9ICJDdW11bGF0aXZlIFllYXJseSBTaG9vdGluZ3MgYW5kIERlYXRoc1xuQXR0cmlidXRhYmxlIHRvIFNjaG9vbCBTaG9vdGluZ3MiLA0KICAgICAgICAgc3VidGl0bGUgPSBpbnB1dCRzdGF0ZV9zZWxlY3RlZCwNCiAgICAgICAgIHkgPSAiQ3VtdWxhdGl2ZSBudW1iZXIgb2YgZXZlbnRzIiwNCiAgICAgICAgIHggPSAiWWVhciIpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ImNvcm5mbG93ZXJibHVlIiksDQogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gJ3doaXRlJywgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkNCg0KfSkNCg0KJycnDQoNCiMjIyBEZWF0aHMgUGVyIFNob290aW5nDQoNCicnJ3tyfQ0KDQpyZW5kZXJQbG90KHsNCg0Kc2hvb3RpbmdfZGF0YV9zdGF0ZSA8LSBzaG9vdGluZ19kYXRhICU+JSBmaWx0ZXIoU3RhdGUgPT0gaW5wdXQkc3RhdGVfc2VsZWN0ZWQpDQpsaWJyYXJ5KHRpZHlyKQ0KZGVhdGhzX3BlcmNfZXZlbnQgPC1zaG9vdGluZ19kYXRhX3N0YXRlICU+JQ0KICAgY291bnQoYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgIHJlbmFtZSgibnVtX2V2ZW50cyI9IG4pICU+JQ0KICAgICB0aWR5cjo6ZHJvcF9uYSgpICU+JQ0KICAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZChudW1fZXZlbnRzL3N1bShudW1fZXZlbnRzKSoxMDAsIGRpZ2l0cyA9MSkpDQoNCmdyZWF0ZXJfdGhhbjQgPC0gDQogIGRlYXRoc19wZXJjX2V2ZW50ICU+JSANCiAgZmlsdGVyKGBLaWxsZWQgKGluY2x1ZGVzIHNob290ZXIpYCA+PSA0KSAlPiUgDQogIGNvbFN1bXMoKQ0KDQpkZWF0aHNfcGVyY19ldmVudCAlPD4lIGJpbmRfcm93cyhncmVhdGVyX3RoYW40KQ0KDQpkZWF0aHNfcGVyY19ldmVudCAlPD4lIA0KICBtdXRhdGUoY2F0ZWdvcnkgPSBwYXN0ZTAoYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgLCAiIGRlYXRocyAiLCAiXG4oIiwgcGVyY2VudCwgIiUpIikpIA0KDQpkZWF0aHNfcGVyY19ldmVudCAlPD4lIA0KICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oDQogICAgY2F0ZWdvcnkgPT0gIGxhc3QocHVsbChkZWF0aHNfcGVyY19ldmVudCwgY2F0ZWdvcnkpKSB+IHBhc3RlMCgiNCsgZGVhdGhzICIsICJcbigiLCBwZXJjZW50LCAiJSkiKSwNCiAgICBjYXRlZ29yeSA9PSAiMSBkZWF0aHMiIH4gIjEgZGVhdGgiLA0KICAgIFRSVUUgfiBjYXRlZ29yeSkpDQoNCmRlYXRoc19wZXJjX2V2ZW50ICU8PiUgDQogIGZpbHRlciAoIWR1cGxpY2F0ZWQoY2F0ZWdvcnkpKQ0KDQpkZWF0aHNfcGVyY19ldmVudCAlPiUgDQogIHNlbGVjdCgtYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoY2F0ZWdvcnksICIwIGRlYXRoc3wxIGRlYXRofDIgZGVhdGhzfDMgZGVhdGhzfDRcXCsiKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gcm91bmQocGVyY2VudCkpICU+JQ0KICBzZWxlY3QoLW51bV9ldmVudHMpICU+JQ0KICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNhdGVnb3J5LCANCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwZXJjZW50KSAlPiUNCiAgd2FmZmxlOjp3YWZmbGUobGVnZW5kX3BvcyA9ICJib3R0b20iLCB0aXRsZSA9ICJEZWF0aHMgUGVyIFNjaG9vbCBTaG9vdGluZyIsIA0KICAgICAgIHhsYWI9IjEgc3F1YXJlIH4gMSUiKSsgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkNCg0KfSkNCg0KJycnDQpgYGANCg0KSW4gdGhlIHRoaXJkIGNvbHVtbiwgdGhlIHN0YXRlIHNwZWNpZmljIHN0YXRpc3RpY3MgYXJlIGRpc3BsYXllZC4gU29tZSBvZiB0aGVzZSBhcmUgc3RhdGljLCB3aGlsZSBvdGhlcnMgdXBkYXRlIGZvciB0aGUgc3RhdGUgc2VsZWN0ZWQuIFRvIGNhbGN1bGF0ZSBzb21lIG9mIHRoZXNlIHdlIHdpbGwgYWxzbyB1c2UgZGF0YSBmb3JtIHRoZSBgcG9saXNjaWRhdGFgIGZ1bmN0aW9uIHRvIGdldCB0aGUgc3RhdGUgcG9wdWxhdGlvbiB2YWx1ZXMgaW4gMjAxMC4gVGhlIGBwb3AyMDEwX2h1bl90aG91YCB2YXJpYWJsZSBpcyB0aGUgcG9wdWxhdGlvbiBpbiB0ZXJtcyBvZiAxMDAsMDAwIHBlb3BsZS4gIA0KDQpgYGANCkNvbHVtbiB7ZGF0YS13aWR0aD00NTB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipUb3RhbCBTdGF0ZSBEZWF0aHMqKg0KDQonJyd7cn0NCnJlbmRlclZhbHVlQm94KHsNCnNob290aW5nX2RhdGFfc3RhdGUgPC0gc2hvb3RpbmdfZGF0YSAlPiUgZmlsdGVyKFN0YXRlID09IGlucHV0JHN0YXRlX3NlbGVjdGVkKQ0KDQoNCnZhbHVlQm94KHN1bShwdWxsKHNob290aW5nX2RhdGFfc3RhdGUsYEtpbGxlZCAoaW5jbHVkZXMgc2hvb3RlcilgKSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIGNvbG9yID0gIndoaXRlIikNCn0pDQonJycNCg0KIyMjICoqVVMgU3RhdGUgQXZlcmFnZSBEZWF0aCBDb3VudCoqDQoNCicnJ3tyfQ0Kc2hvb3RpbmdfZGF0YV9zdGF0ZSA8LXNob290aW5nX2RhdGEgJT4lIA0KICBncm91cF9ieShTdGF0ZV9hYmIsIFN0YXRlKSAlPiUNCiAgY291bnQobmEucm0gPSBUUlVFKSAlPiUNCiAgcmVuYW1lKHNob290aW5ncyA9IG4pICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShzdGF0ZV9zdW0gPSBzdW0oc2hvb3RpbmdzKSkgJT4lDQogIG11dGF0ZShzdGF0ZV9hdmcgPSBzdGF0ZV9zdW0vNTApDQoNCnN0YXRlX2RhdGEgPC0gcG9saXNjaWRhdGE6OnN0YXRlcw0Kc3RhdGVfZGF0YSAlPD4lDQogIHNlbGVjdChzdGF0ZWlkLCBwb3AyMDEwLCBwb3AyMDEwX2h1bl90aG91KSAlPiUNCiAgbXV0YXRlKHN0YXRlaWQgPSBhcy5jaGFyYWN0ZXIoc3RhdGVpZCkpJT4lDQogIG11dGF0ZShzdGF0ZWlkID0gc3RyX3JlbW92ZV9hbGwoc3RhdGVpZCwgcGF0dGVybiA9ICIgIikpDQoNCnNob290aW5nX2RhdGFfc3RhdGU8LWxlZnRfam9pbihzaG9vdGluZ19kYXRhX3N0YXRlLCBzdGF0ZV9kYXRhLCBieSA9IGMoIlN0YXRlX2FiYiIgPSAic3RhdGVpZCIpKQ0KDQpkZWF0aHNfU3RhdGUgPC1zaG9vdGluZ19kYXRhICU+JSANCiAgZ3JvdXBfYnkoU3RhdGUpICU+JQ0KICBzdW1tYXJpemUoIGRlYXRocyA9IHN1bShgS2lsbGVkIChpbmNsdWRlcyBzaG9vdGVyKWAsIG5hLnJtID0gVFJVRSkpDQoNCnN0YXRlX2RhdGEgPC0gbGVmdF9qb2luKHNob290aW5nX2RhdGFfc3RhdGUsIGRlYXRoc19TdGF0ZSkNCg0KVVNhdmcgPC0gcm91bmQobWVhbihwdWxsKHN0YXRlX2RhdGEsIGRlYXRocyksIG5hLnJtID0gVFJVRSksIDIpDQp2YWx1ZUJveChVU2F2ZywgY29sb3IgPSAid2hpdGUiKQ0KJycnDQoNCg0KIyMjICoqU3RhdGUgRGVhdGggUmF0ZSAocGVyIDEwMCwwMDAgcGVvcGxlKSoqDQoNCicnJ3tyfQ0Kc3RhdGVfZGF0YSAlPD4lDQogIG11dGF0ZShwZXJjYXBpdGFfZGVhdGhzICA9IGRlYXRocy9wb3AyMDEwX2h1bl90aG91KQ0KDQpyZW5kZXJWYWx1ZUJveCh7DQogIA0KICBzaG9vdGluZ19kYXRhX3N0YXRlIDwtIHN0YXRlX2RhdGEgJT4lIGZpbHRlcihTdGF0ZSA9PSBpbnB1dCRzdGF0ZV9zZWxlY3RlZCkNCg0KICB2YWx1ZUJveChmb3JtYXQocm91bmQocHVsbChzaG9vdGluZ19kYXRhX3N0YXRlLCBwZXJjYXBpdGFfZGVhdGhzKSwgZGlnaXRzID0gMyksIG5zbWFsbCA9IDMpLA0KICAgICAgICAgY29sb3IgPSAid2hpdGUiKQ0KfSkNCg0KDQonJycNCg0KIyMjICoqVVMgTmF0aW9uYWwgRGVhdGggUmF0ZSAocGVyIDEwMCwwMDAgcGVvcGxlKSoqDQoNCicnJ3tyfQ0KcmVuZGVyVmFsdWVCb3goew0KDQogVVNfcGVyY2FwIDwtc3VtbWFyaXplKHN0YXRlX2RhdGEsIHN1bShkZWF0aHMsIG5hLnJtID0gVFJVRSkpLyAoc3VtbWFyaXplKHN0YXRlX2RhdGEsc3VtKHBvcDIwMTAsIG5hLnJtID0gVFJVRSkpIC8xMDAwMDApDQogDQp2YWx1ZUJveCh2YWx1ZSA9IHJvdW5kKFVTX3BlcmNhcCwgZGlnaXRzID0gMyksDQogICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpDQp9KQ0KJycnDQoNCiMjIyAqKlN0YXRlIFNob290aW5nIFJhdGUgKHBlciAxMDAsMDAwIHBlb3BsZSkqKg0KDQonJyd7cn0NCg0Kc3RhdGVfZGF0YSAlPD4lDQogIG11dGF0ZShwZXJjYXBpdGFfc2hvb3RpbmdzICA9IHNob290aW5ncy9wb3AyMDEwX2h1bl90aG91KQ0KDQpyZW5kZXJWYWx1ZUJveCh7DQogIA0KICBzaG9vdGluZ19kYXRhX3N0YXRlIDwtIHN0YXRlX2RhdGEgJT4lIGZpbHRlcihTdGF0ZSA9PSBpbnB1dCRzdGF0ZV9zZWxlY3RlZCkNCg0KdmFsdWVCb3goZm9ybWF0KHJvdW5kKHB1bGwoc2hvb3RpbmdfZGF0YV9zdGF0ZSwgcGVyY2FwaXRhX3Nob290aW5ncyksIGRpZ2l0cyA9IDMpLCBuc21hbGwgPSAzKSwNCiAgICAgICAgIGNvbG9yID0gIndoaXRlIikNCn0pDQoNCg0KJycnDQoNCiMjIyAqKlVTIE5hdGlvbmFsIFNob290aW5nIFJhdGUgKHBlciAxMDAsMDAwIHBlb3BsZSkqKg0KDQonJyd7cn0NCnJlbmRlclZhbHVlQm94KHsNCg0KIFVTX3BlcmNhcCA8LXN1bW1hcml6ZShzdGF0ZV9kYXRhLCBzdW0oc2hvb3RpbmdzLCBuYS5ybSA9IFRSVUUpKS8gKHN1bW1hcml6ZShzdGF0ZV9kYXRhLHN1bShwb3AyMDEwLCBuYS5ybSA9IFRSVUUpKSAvMTAwMDAwKQ0KIA0KdmFsdWVCb3godmFsdWUgPSByb3VuZChVU19wZXJjYXAsIGRpZ2l0cyA9IDMpLA0KICAgICAgICAgY29sb3IgPSAid2hpdGUiKQ0KfSkNCicnJw0KDQojIyMNCg0KUGVyIGNhcGl0YSBjYWxjdWxhdGlvbnMgYXJlIGJhc2VkIG9uIDIwMTAgcG9wdWxhdGlvbiB2YWx1ZXMuDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KIyMgICoqVGhlIE1hcCBwYWdlIChJbnRlcmFjdGl2ZSkqKg0KKioqDQoNCk5leHQsIHdlIGNyZWF0ZSBvdXIgbWFwIHBhZ2UuIFByZXZpb3VzbHksIGluIHRoZSAgWyoqRGF0YSBFeHBsb3JhdGlvbiBhbmQgV3JhbmdsaW5nKipdIHNlY3Rpb24sIHdlIGdlb2NvZGVkIG91ciBkYXRhIGFuZCBtb2RpZmllZCB0aGUgYGxhdGl0dWRlYCBhbmQgYGxvbmdpdHVkZWAgdmFyaWFibGVzIHNvIHRoYXQgZXZlbnRzIHRoYXQgb2NjdXJyZWQgaW4gdGhlIHNhbWUgbG9jYXRpb24gd291bGQgaGF2ZSBzbGlnaHRseSBkaWZmZXJlbnQgdmFsdWVzIHNvIHRoYXQgdGhleSB3aWxsIG5vdCBjb3ZlciBvbmUgYW5vdGhlciBpbiBvdXIgbWFwLg0KDQpUbyBjcmVhdGUgb3VyIG1hcCwgd2Ugd2lsbCB1c2UgdGhlIGBsZWFmbGV0YCBwYWNrYWdlIHdoaWNoIHVzZXMgdGhlIFtMZWFmbGV0XShodHRwczovL2xlYWZsZXRqcy5jb20vKSAgSmF2YVNjcmlwdCBsaWJyYXJ5LiANCg0KIyMjICoqTGVhZmxldCoqDQoqKioNCg0KYExlYWZsZXRgIHdvcmtzIGJ5IHByb3ZpZGVkIGJ5IGFkZGluZyBiYXNlIGRhdGEgKHN1Y2ggYXMgYSBtYXApIGFuZCB0aGVuIGFkZGluZyBtYXJrZXJzIGlmIGRlc2lyZWQgaW4gbGF5ZXJzLiBUaGlzIGlzIHZlcnkgc2ltaWxhciB0byBob3cgYGdncGxvdDJgIGZ1bmN0aW9ucyAocHVuIGludGVuZGVkKS4NCg0KVGhlIGxheWVycyBkaXNwbGF5ZWQgY2FuIGJlIGNvbnRyb2xsZWQgdXNpbmcgYSBzb3J0IG9mIGxlZ2VuZC4gRGVwZW5kaW5nIG9uIHRoZSB0eXBlIG9mIGxheWVycywgc29tZSBpbmZvcm1hdGlvbiBtYXkgYmUgZGlzcGxheWVkIG11dHVhbGx5IGV4Y2x1c2l2ZSBvZiB0aGUgb3RoZXIgbGF5ZXJzOyBvdGhlciBsYXllcnMgKHN1Y2ggYXMgY2lyY2xlcy9nZW5lcmFsIG1hcmtlcnMpIGNhbiBiZSB0b2dnbGVkIG9uIGFuZCBvZmYuIA0KDQpDbHVzdGVyaW5nIG9wdGlvbnMgY2FuIGFsc28gYmUgYXBwbGllZCB0byBjaXJjbGVzL21hcmtlcnMuIFNvbWUgZXhhbXBsZXMgb2YgdGhpcyBjYW4gYmUgZm91bmQgb24gdGhlIGJvdHRvbSBvZiBbdGhpcyB3ZWJzaXRlXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvbWFya2Vycy5odG1sKS4NCg0KVGhlIGBncm91cHNgIGluIGxlYWZsZXQgY2FuIGJlIHRob3VnaHQgb2YgYXMgbGF5ZXItc3BlY2lmaWMgSURzIHRoYXQgY3JlYXRlIGxhYmVscyBmb3IgbGVnZW5kcyBhbmQgYWxsb3cgc3BlY2lmaWMgbGF5ZXJzIHRvIGJlIHJlZmVycmVkIHRvIGluIHNlcGFyYXRlIGZ1bmN0aW9ucy4gDQoNClRodXMsIGlmIHdlIGNhbGxlZCBhIGdyb3VwICJMYXllciAxIiBhbmQgdGhlbiBpbiBhIHN1YnNlcXVlbnQgbGF5ZXIgcmVmZXIgdG8gIkxheWVyIDEiLCBgbGVhZmxldGAgd2lsbCBjb3JyZWN0bHkgaWRlbnRpZnkgd2hpY2ggbGF5ZXIgaXMgYmVpbmcgcmVmZXJlbmNlZC4NCg0KTm90ZSB0aGF0IGBsZWFmbGV0YCBjYW4gcmVxdWlyZSBhIGxvdCBvZiBjb21wdXRhdGlvbmFsIHBvd2VyIGRlcGVuZGluZyBvbiB0aGUgdHlwZXMgb2YgbWFwcyBwcm9kdWNlZC4NCg0KIyMjICoqTG9vayoqDQoqKioNCg0KVGhpcyBpcyB3aGF0IHRoZSBwYWdlIHdpbGwgbG9vayBsaWtlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAiVGhlX01hcF9QYWdlLnBuZyIpKQ0KYGBgDQoNCiMjIyAqKk92ZXJhbGwgU3RydWN0dXJlKioNCioqKg0KDQpUaGUgb3ZlcmFsbCBzdHJ1Y3R1cmUgZm9yIHRoaXMgcGFnZSBpcyBzaW1wbGUuIFRoZXJlIGlzIGp1c3Qgb25lIGNvbHVtbiAsd2hpY2ggd2lsbCBjb250YWluIHRoZSBtYXAuDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiAsICJtYXBfcGFnZV9zdHJ1Y3R1cmUucG5nIikpDQpgYGANCg0KIyMjICoqRGV0YWlscyoqDQoqKioNCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIHRoZSBjb2RlIGZvciB0aGlzIHBhZ2UuIDwvc3VtbWFyeT4NCg0KRmlyc3QsIHdlIGNyZWF0ZSBhIHNtYWxsZXIgZGF0YXNldCB0aGF0IGp1c3QgaW5jbHVkZXMgdGhlIGRhdGEgdGhhdCB3ZSB3YW50IHRvIHVzZSBpbiB0aGUgbWFwLiBXZSB3aWxsIGluY2x1ZGUgdGhlIGRhdGUsIHRoZSBuYW1lIG9mIHRoZSBzY2hvb2wgYW5kIHRoZSBuYXJyYXRpdmUgZm9yIGVhY2ggcG9pbnQgYXMgYSBbcG9wdXBdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC9wb3B1cHMuaHRtbCkgdGhhdCB3aWxsIGJlIHNob3duIHdoZW4gdGhlIHVzZXIgaG92ZXJzIG92ZXIgYSBwb2ludC4gDQoNCldlIG5lZWQgdG8gZG8gdGhpcyB1c2luZyBbSFRNTF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSFRNTCkgY29kZSBhcyB0aGUgYGxlYWZsZXRgIHBhY2thZ2Ugd2lsbCB1bHRpbWF0ZWx5IHJlbmRlciB0aGUgbWFwIHVzaW5nIHRoaXMgbGFuZ3VhZ2UuDQoNCldlIHVzZSB0aGUgYHBhc3RlKClgIGZ1bmN0aW9uIHRvIGNvbWJpbmUgdGhlc2UgZWxlbWVudHMgYXMgd2VsbCBhcyBIVE1MIGNvZGUgdG8gY3JlYXRlIGxpbmUgYnJlYWtzIGFuZCBib2xkIHRoZSBuYW1lIG9mIHRoZSBzY2hvb2wuDQoNClRvIGNyZWF0ZSBsaW5lIGJyZWFrcyBpbiBIVE1MLCB0aGUgYDxicj5gIHN5bnRheCBpcyB1c2VkLiBUaGlzIGlzIHVzZWQgdG8gc2VwYXJhdGUgZWFjaCBwYXJ0IG9mIHRoZSBlbGVtZW50cyB0aGF0IGFyZSBnZXR0aW5nIHBhc3RlZCB0b2dldGhlciB3aXRoIHRoZSBiYXNlIGBwYXN0ZSgpYCBmdW5jdGlvbiBieSBiZWluZyBzcGVjaWZpZWQgYXMgdGhlIHNlcGFyYXRvciB3aXRoIHRoZSBgc2VwYCBhcmd1bWVudC4NCg0KVG8gY3JlYXRlIGJvbGQgZm9udCBpbiBIVE1MLCB0aGUgdGV4dCBpcyBzdXJyb3VuZGVkIGJ5IGA8Yj5gIGFuZCBgPC9iPmAgbGlrZSBzbzogYDxiPiBCb2xkIHRleHQgPC9iPmAuIFRodXMgb25seSB0aGUgc2Nob29sIG5hbWUgaXMgaW4gYm9sZC4NCg0KRmluYWxseSwgdGhlIGA8ZGl2PmAgYW5kIGA8L2Rpdj5gIGFyZSBjb250ZW50IGRpdmlkZXJzIGluIEhUTUwuICBUaGV5IHNlcGFyYXRlIHRoZSBpbmRpdmlkdWFsIHNjaG9vbCBzaG9vdGluZyBldmVudCBpbmZvcm1hdGlvbiBzZWN0aW9ucyB0aGF0IHdpbGwgYmUgcGxvdHRlZCBvbiB0aGUgbWFwLiBUaGUgZmlyc3QgZGl2aWRlciBjYW4gYWxzbyB0YWtlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzdHlsZSBvZiB0aGUgb3V0cHV0LiBUaGlzIHVzZXMgW0NTU10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQ1NTKSBjb2RlLCB3aGljaCBpcyB3aGF0IGlzIHVzZWQgdG8gc3R5bGl6ZSBIVE1MLiANCg0KVGhlIGNvZGUgaGVyZSBzdGF0ZXMgdGhhdCB0aGUgaGVpZ2h0IG9mIHRoZSB0ZXh0IGJveCBmb3IgZWFjaCBldmVudCBzaG91bGQgaGF2ZSBhIGhlaWdodCB0aGF0IGlzIHByb3BvcnRpb25hbCB0byB0aGUgdGV4dCwgdGhhdCB0aGUgW2hlaWdodCBvZiBlYWNoIGxpbmVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9saW5lLWhlaWdodCkgc2hvdWxkIGJlIG9mIDEgW2VtIHVuaXRzXShodHRwczovL3d3dy53My5vcmcvU3R5bGUvRXhhbXBsZXMvMDA3L3VuaXRzLmVuLmh0bWwpIChlbSBzdGFuZHMgZm9yIGVsZW1lbnQpLiBIZW5jZSwgMSB1bml0IHJlbGF0aXZlIHRvIHRoZSBzaXplIG9mIHRoZSBlbGVtZW50LiBUaGVyZWZvcmUgZ2FwcyBiZXR3ZWVuIGxpbmVzIGFyZSB0aGUgc2FtZSBoZWlnaHQgYXMgdGhlIGxpbmVzIG9mIHRleHQuIFRoZSBgb3ZlcmZsb3c6dmlzaWJsZWAgY29kZSBzcGVjaWZpZXMgd2hhdCB0byBkbyBpbiBjYXNlIHRoZSB0ZXh0IGJveCB0ZXh0IGlzIHRvbyBsYXJnZSAtIGluIHRoaXMgY2FzZSB1c2VycyBjYW4gc2Nyb2xsIChzZWUgW2hlcmVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9vdmVyZmxvdykgZm9yIG1vcmUgb3B0aW9ucyksIGFuZCB0aGUgW3BhZGRpbmddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9wYWRkaW5nKSBzcGVjaWZpY2F0aW9uIHNldHMgdGhlIHNpemUgb2YgdGhlIG1hcmdpbnMgb2YgdGhlIHRleHQgYm94IGFyb3VuZCB0aGUgdGV4dC4gIA0KDQpTZWUgdGhpcyBbd2Vic2l0ZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSFRNTC9FbGVtZW50L2RpdikgdG8gbGVhcm4gbW9yZSBhYm91dCBIVE1MIGNvZGUuDQoNCmBgYA0KTWFwIHtkYXRhLWljb249ImZhLW1hcCJ9DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IA0KQ29sdW1uDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIA0KICAgIA0KIyMjIA0KDQpUaGlzIG1hcCBzaG93cyB3aGVyZSBzY2hvb2wgc2hvb3RpbmdzIHRvb2sgcGxhY2UgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgYmV0d2VlbiBKYW51YXJ5IDE5NzAgdG8gSnVuZSAyMDIwIGFjY29yZGluZyB0byB0aGUgdGhlIG9wZW4tc291cmNlIFtDZW50ZXIgZm9yIEhvbWVsYW5kIERlZmVuc2UgYW5kIFNlY3VyaXR5XShodHRwczovL3d3dy5jaGRzLnVzL2MvKSAoQ0hEUykgW0stMTIgU2Nob29sIFNob290aW5nIERhdGFiYXNlXShodHRwczovL3d3dy5jaGRzLnVzL3NzZGIvZGF0YXNldC8pLiBDbGljayB0aGUgY2lyY2xlcyBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCg0KICAgIA0KJycne3J9DQojIHNwZWNpZnkgdGhlIHBvcHVwcw0KDQpzaG9vdGluZ19pbmZvcm1hdGlvbjAgPC0gcGFzdGUoJzxkaXYgc3R5bGU9ImhlaWdodDphdXRvO2xpbmUtaGVpZ2h0OjFlbTtvdmVyZmxvdzpzY3JvbGw7cGFkZGluZzoxZW0iPicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG9vdGluZ19kYXRhX2dlb2NvZGVkJERhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob290aW5nX2RhdGFfZ2VvY29kZWQkU2Nob29sLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjwvYj4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvb3RpbmdfZGF0YV9nZW9jb2RlZCRgTmFycmF0aXZlIChEZXRhaWxlZCBTdW1tYXJ5LyBCYWNrZ3JvdW5kKWAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPC9kaXY+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICI8YnI+IikNCg0KDQonJycNCmBgYA0KDQpUaGUgbmV4dCBiaXQgb2YgY29kZSB0aGVuIHVzZXMgdGhpcyBkYXRhIGFuZCB0aGUgYHNob290aW5nX2RhdGFfZ2VvY2RlZGAgdG8gYWN0dWFsbHkgY3JlYXRlIHRoZSBtYXAhDQoNClRoZSBgbGVhZmxldCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgbGVhZmxldGAgcGFja2FnZSBjcmVhdGVzIGEgTGVhZmxldCBtYXAgW3dpZGdldF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2ViX3dpZGdldCkgdXNpbmcgdGhlICBbYGh0bWx3aWRnZXRzYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2h0bWx3aWRnZXRzL2luZGV4Lmh0bWwpIHBhY2thZ2UsIHdoaWNoIGFsbG93cyB0aGUgbWFwIHRvIGJlIHJlbmRlcmVkIGFzIGFuIGFwcGxpY2F0aW9uIHdpdGhpbiBIVE1MIHdlYnNpdGVzLg0KDQpUaGlzIGZpcnN0IGxpbmUgb2YgY29kZSBzdGFydHMgdGhlIHByb2Nlc3Mgb2YgbWFraW5nIHRoZSB3aWRnZXQsIGJ1dCBqdXN0IGxpa2UgdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gZnJvbSBgZ2dwbG90MmAgaXQgY3JlYXRlcyBhbiBlbXB0eSBtYXAgYW5kIGxheWVycyBuZWVkIHRvIGJlIGFkZGVkLg0KDQpUaGUgYGFkZFByb3ZpZGVyVGlsZXMoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGxlYWZsZXQoKWAgcGFja2FnZSBkb2VzIGp1c3QgdGhhdCwgYnkgYWRkaW5nIHRoZSBtYXAgYmFja2dyb3VuZC4gV2Ugd2lsbCBhZGQgdGhyZWUgZGlmZmVyZW50IGtpbmRzIG9mIG1hcCBiYWNrZ3JvdW5kcy4gU2VlIFtoZXJlXShodHRwOi8vbGVhZmxldC1leHRyYXMuZ2l0aHViLmlvL2xlYWZsZXQtcHJvdmlkZXJzL3ByZXZpZXcvaW5kZXguaHRtbCkgZm9yIGFsbCB0aGUgb3B0aW9ucyBvZiBwcm92aWRlcnMgd2hpY2ggY3JlYXRlIGEgdmFyaWV0eSBvZiBkaXN0aW5jdCBiYWNrZ3JvdW5kcyBhbmQgdGhlbiB0aGUgYGdyb3VwYCBhcmd1bWVudCBuYW1lcyBlYWNoIG9mIHRoZXNlIGxheWVycyB0byBiZSByZWZlcnJlZCB0byBsYXRlci4gVGhlIGxhc3QgbGF5ZXIgYWRkZWQgd2lsbCBiZSB0aGUgb25lIHNob3duIGJ5IGRlZmF1bHQuDQoNCkF0IHRoaXMgcG9pbnQgd2Ugc3RpbGwgb25seSBoYXZlIGEgbWFwIGluIGdlbmVyYWwuIE5vdyB3ZSBuZWVkIHRvIGFkZCB0aGUgZGF0YSBhYm91dCBzY2hvb2wgc2hvb3RpbmcgZXZlbnRzLiANCg0KVG8gZG8gdGhpcywgd2UgYWRkIG1hcmtlcnMgdG8gdGhlIHBsb3QgdXNpbmcgdGhlIGBhZGRDaXJjbGVNYXJrZXJzKClgIGZ1bmN0aW9uLiBUaGlzIGZ1bmN0aW9uIHRha2VzIG1hbnkgZGlmZmVyZW50IGFyZ3VtZW50cy4gU2VlIGRldGFpbHMgYWJvdXQgdGhlbSBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2xlYWZsZXQvbGVhZmxldC5wZGYpLiANCg0KSW1wb3J0YW50bHksIHdlIG5lZWQgdG8gc3BlY2lmeSB3aGF0IHZhcmlhYmxlcyBpbiBvdXIgcHJvdmlkZWQgZGF0YSBgc2hvb3RpbmdfZGF0YV9nZW9jb2RlZGAgY29udGFpbnMgdGhlIGxvbmdpdHVkZSB2YWx1ZXMgKGBsbmdgKSBhbmQgdGhlIGxhdGl0dWRlIHZhbHVlcyAoYGxhdGApLiANCg0KV2Ugd2lsbCBhbHNvIHVzZSB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czogIA0KDQotIGByYWRpdXNgIC0gYXJndW1lbnQgc3BlY2lmaWVzIGhvdyBsYXJnZSB0aGUgY2lyY2xlcyBmb3IgdGhlIHBvaW50cyB3aWxsIGJlICANCi0gYGNvbG9yYCAtIGFyZ3VtZW50IHNwZWNpZmllcyB0aGUgY29sb3Igb2YgdGhlIGluZGl2aWR1YWwgcG9pbnRzICANCi0gYGZpbGxPcGFjaXR5YCAtIGFyZ3VtZW50IGFsbG93cyBmb3IgdGhlIGZpbGxpbmcgb2YgdGhlIHBvaW50cyB0byBhIGJpdCB0cmFuc2x1Y2VudCBpZiBzZXQgYmVsb3cgMSAgDQotIGBjbHVzdGVyT3B0aW9uc2AgLSBhcmd1bWVudCBjYW4gYmUgdXNlZCB0byBjbHVzdGVyIHBvaW50cyB0b2dldGhlciBpbnRvIGxhcmdlciBjaXJjbGVzICANCi0gYGdyb3VwYCAtIGFyZ3VtZW50IHNwZWNpZmllcyB3aGF0IHRoZSBwb2ludHMgc2hvdWxkIGJlIGNhbGxlZCBpbiB0aGUgbGVnZW5kIGFuZCB3aGF0IHRoaXMgbGF5ZXIgc2hvdWxkIGJlIHJlZmVycmVkIHRvIGFzIGZvciBsYXRlciB1c2UgIA0KDQpXZSBhbHNvIGFkZCBhIG1pbmkgbWFwIHVzaW5nIHRoZSBgYWRkTWluaU1hcCgpYCBmdW5jdGlvbiwgd2hpY2ggY2FuIGJlIHVzZWZ1bCB0byBzZWUgd2hlcmUgeW91IGFyZSBvbiB0aGUgbWFwLiBUaGUgdHlwZSBvZiBwbG90IHN0eWxlIHRvIHVzZSBmb3IgdGhlIG1pbmkgbWFwIGlzIHNwZWNpZmllZCB3aXRoIHRoZSBgdGlsZXNgIGFyZ3VtZW50IGFuZCB0aGUgYHRvZ2dsZURpc3BsYXlgIGFyZ3VtZW50IGFsbG93cyBmb3IgdGhlIHVzZXIgdG8gcmVtb3ZlIHRoaXMgZmVhdHVyZS4NCg0KSW1wb3J0YW50bHksIHRoZSBgYWRkTGF5ZXJzQ29udHJvbCgpYCBmdW5jdGlvbiAgYWxsb3dzIHVzZXJzIHRvIHRvZ2dsZSBiZXR3ZWVuIGRpZmZlcmVudCBiYWNrZ3JvdW5kcyBhbmQgbWFya2Vycy4gSW4gb3VyIGNhc2Ugd2UgaGF2ZSB0aHJlZSBkaWZmZXJlbnQgYmFja2dyb3VuZCBsYXllcnMgd2hpY2ggYXJlIHJlZmVycmVkIHRvIGFzIGBiYXNlR3JvdXBzYCBhbmQgd2UgaGF2ZSBvbmUgYG92ZXJsYXlHcm91cHNgIHdoaWNoIGlzIG91ciBjaXJjbGUgbWFya2VycyBmb3Igc2Nob29sIHNob290aW5nIGV2ZW50cy4gVGhlIGdyb3VwIG5hbWVzIGZvciB0aGVzZSBuZWVkIHRvIGJlIGlkZW50aWZpZWQgdG8gYWxsb3cgdXNlcnMgdG8gdG9nZ2xlIGJldHdlZW4gdGhlbS4gDQoNClRoZSBgc2V0X3ZpZXcoKWAgZnVuY3Rpb24gYWxsb3dzIGZvciB0aGUgc3RhcnRpbmcgcG9zaXRpb24gYW5kIHpvb20gdG8gYmUgbW9kaWZpZWQuIFRoaXMgYWxsb3dzIHVzIHRvIGNlbnRlciB0aGUgbWFwIGFyb3VuZCB0aGUgY29udGluZW50YWwgVVMuDQoNCmBgYA0KJycne3J9DQpsZWFmbGV0KHNob290aW5nX2RhdGFfZm9yX21hcCkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXIgPSBwcm92aWRlcnMkT3BlblN0cmVldE1hcCwgZ3JvdXAgPSAiT3BlblN0cmVldE1hcCIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gcHJvdmlkZXJzJEVzcmkuV29ybGRJbWFnZXJ5LCBncm91cCA9ICJFU1JJIFdvcmxkIEltYWdlcnkiKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlciA9IHByb3ZpZGVycyRTdGFtZW4uVG9uZXJMaXRlLCBncm91cCA9ICJUb25lciIpJT4lDQogIGFkZENpcmNsZU1hcmtlcnMocG9wdXAgPSB+c2hvb3RpbmdfaW5mb3JtYXRpb24wLA0KICAgICAgICAgICAgICAgICAgICAgbG5nID0gfmxvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgIGxhdCA9IH5sYXRpdHVkZSwNCiAgICAgcmFkaXVzID0gNSwNCiAgICAgY29sb3IgPSAicmVkIiwNCiAgICAgZmlsbE9wYWNpdHkgPSAwLjIsDQogICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSwNCiAgICAgZ3JvdXAgPSAiQ2lyY2xlcyIpICU+JQ0KICBhZGRNaW5pTWFwKHRpbGVzID0gcHJvdmlkZXJzJFN0YW1lbi5Ub25lciwNCiAgICAgICAgICAgICAgdG9nZ2xlRGlzcGxheSA9IFRSVUUpICU+JQ0KICBhZGRMYXllcnNDb250cm9sKA0KICAgICBiYXNlR3JvdXBzID0gYygiVG9uZXIgTGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICJPcGVuU3RyZWV0TWFwIiwNCiAgICAgICAgICAgICAgICAgICAgIkVTUkkgV29ybGQgSW1hZ2VyeSIpLA0KICAgICBvdmVybGF5R3JvdXBzID0gYygiQ2lyY2xlcyIpKSAlPiUNCiAgIHNldFZpZXcobG5nID0gLTk4LjM1LCBsYXQgPSAzOS41LCB6b29tID0gNCkNCicnJw0KYGBgDQoNCjwvZGV0YWlscz4NCiANCioqKg0KIA0KDQojIyAqKlRoZSBUdXRvcmlhbCBQYWdlKioNCioqKg0KDQpIZXJlLCB3ZSBjcmVhdGUgYSAqKlR1dG9yaWFsKiogcGFnZSB0aGF0IGxpbmtzIHRvIHRoaXMgY2FzZSBzdHVkeS4gVGhpcyBwcm92aWRlcyBhIHNpbXBsZSBvdmVydmlldyBvZiBob3cgd2UgY3JlYXRlZCB0aGUgZGFzaGJvYXJkLiANCg0KIyMjICoqTG9vayoqDQoqKioNCg0KVGhpcyBpcyB3aGF0IHRoZSBwYWdlIHdpbGwgbG9vayBsaWtlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAidGhlX1R1dG9yaWFsX1BhZ2UucG5nIikpDQpgYGANCg0KDQojIyMgKipPdmVyYWxsIFN0cnVjdHVyZSoqDQoqKioNCg0KVG8gY3JlYXRlIHRoaXMgcGFnZSB3ZSB3aWxsIHVzZSBhIHNwZWNpYWwgbGF5b3V0IGNhbGxlZCBhIFtzdG9yeWJvYXJkXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdG9yeWJvYXJkKS4gU3RvcnkgYm9hcmRzIGFyZSB1c2VkIGluIG1hbnkgb3RoZXIgZmllbGRzLCBidXQgdGhlIGlkZWEgaXMgdGhhdCB0aGVyZSBhcmUgbXVsdGlwbGUgaW1hZ2VzIGluIGEgc2VxdWVuY2UuIFRvIGNyZWF0ZSBvdXIgc3Rvcnlib2FyZCBwYWdlIHdpdGggYGZsZXhkYXNoYm9hcmRgIHdlIHdpbGwgdXNlIGB7LnN0cm95Ym9hcmR9YCBuZXh0IHRvIHRoZSBwYWdlIG5hbWUuIEVhY2ggcGFnZSBuYW1lIHdpbGwgYmUgc3BlY2lmaWVkIHVzaW5nIHRoaXMgc3ludGF4OiBgIyMjYC4gDQoNCkhlcmUgeW91IHNlZSB0aGUgdG9wIHBhcnQgb2YgdGhlIG92ZXJhbGwgc3RydWN0dXJlOg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIgLCAidHV0b3JpYWxTdHJ1Y3R1cmUucG5nIikpDQpgYGANCg0KIyMjICoqRGV0YWlscyoqDQoqKioNCg0KVGhlIGNvZGUgZm9yIHRoaXMgcGFnZSBpcyBzaW1pbGFyIHRvIHRoZSBvdGhlciBwYWdlcywgZXhjZXB0IGZvciB0aGUgc3RvcnkgYm9hcmQgc3RydWN0dXJlLiANCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIHRoZSBjb2RlIGZvciB0aGlzIHBhZ2UuIDwvc3VtbWFyeT4NCg0KYGBgDQpUdXRvcmlhbCB7LnN0b3J5Ym9hcmQgZGF0YS1pY29uPSJmYS1saXN0LW9sIn0NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICAgDQoNCiMjIyAqKjEpKiogTG9hZCB0aGUgYGZsZXhkYXNoYm9hcmRgIHBhY2thZ2UuDQoNCkluc3RhbGwgdGhlIHBhY2thZ2UgKGFuZCBvdGhlciBzdXBwb3J0aW5nIG9wdGlvbmFsIHBhY2thZ2VzKSBpZiB5b3UgZG9uJ3QgaGF2ZSB0aGVtIGluc3RhbGxlZCBhbHJlYWR5Lg0KDQonJyd7ciwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiZmxleGRhc2hib2FyZCIpDQppbnN0YWxsLnBhY2thZ2VzKCJzaGlueSIpDQppbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0IikNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KJycnDQoNCk9uY2UgaW5zdGFsbGVkLCBsb2FkIHRoZSBwYWNrYWdlKHMpIGludG8gdGhlIGBSYCBlbnZpcm9ubWVudC4NCg0KJycne3IsIGVjaG89VFJVRX0NCmxpYnJhcnkoZmxleGRhc2hib2FyZCkNCmxpYnJhcnkoc2hpbnkpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGdncGxvdDIpDQonJycNCg0KVGhpcyBhbGwgbmVlZHMgdG8gYmUgZG9uZSBzZXBhcmF0ZWx5IGluIHRoZSBgUmAgY29uc29sZS4NCg0KIyMjICoqMikqKiBDcmVhdGUgYW4gYFJNRGAgZG9jdW1lbnQuICAgDQoNCkRhc2hib2FyZHMgY2FuIGJlIGNyZWF0ZWQgd2l0aCBgZmxleGRhc2hib2FyZGAgaW4gdGhlIGBIVE1MYCBmb3JtYXQuIA0KDQpUaGVgZmxleGRhc2hib2FyZGAgcGFja2FnZSB1c2VzIGBSTWFya2Rvd25gIHRvIHByb2R1Y2UgZGFzaGJvYXJkcyB0aGF0IGNhbiBjb250YWluIGBSYCBvdXRwdXQuDQoNClRoaXMgbWFrZXMgaXQgcG9zc2libGUgdG8gaW5jbHVkZSBzZXZlcmFsIG1lZGl1bXMgaW4gZGFzaGJvYXJkcyBzdWNoIGFzIHBsb3RzIGNyZWF0ZWQgd2l0aCBgZ2dwbG90MmAgb3IgbWFwcyBjcmVhdGVkIHdpdGggYGxlYWZsZXRgLg0KICAgIA0KIyMjICoqMykqKiBDcmVhdGUgYW4gYXBwcm9wcmlhdGUgYFlBTUxgLg0KDQpUaGUgdXNlIG9mIGBmbGV4ZGFzaGJvYXJkYCBhbHRlcnMgdGhlIHdheSBSIE1hcmtkb3duIGRvY3VtZW50cyBmdW5jdGlvbi4gDQoNClIgTWFya2Rvd24gZG9jdW1lbnRzIGNhbiBiZSByZW5kZXJlZCBpbnRvIG1hbnkgZGlmZmVyZW50IG91dHB1dHMsIG9uZSBvZiB3aGljaCBpcyBhIGRhc2hib2FyZC4gDQpUaGUgYFlBTUxgIGhlYWRlciBzZXRzIHVwIGhvdyB0aGUgZG9jdW1lbnQgb3V0cHV0IHNob3VsZCBiZSBjcmVhdGVkLg0KDQpIZXJlIGlzIGFuIGV4YW1wbGUgb2YgYSBgWUFNTGAgaGVhZGVyIHRoYXQgY3JlYXRlcyBhbiBgSFRNTGAgZG9jdW1lbnQgZnJvbSBhbiBSIE1hcmtkb3duIGRvY3VtZW50Og0KDQoNCi0tLQ0KdGl0bGU6ICJVbnRpdGxlZCINCmF1dGhvcjogIkpvaG4gU21pdGgiDQpkYXRlOiAiOC8xMi8yMDIwIg0Kb3V0cHV0OiBodG1sX2RvY3VtZW50DQotLS0NCg0KDQpXZSB1c2VkIHRoZSBmb2xsb3dpbmcgYFlBTUxgIGZvciB0aGlzIGRhc2hib2FyZCwgd2hpY2ggaW1wb3J0YW50bHkgaW5jbHVkZXMgYGZsZXhkYWhzYm9hcmQ6OmZsZXhfZGFzaGJvYXJkYHdoaWNoIHNwZWNpZmllcyB0aGF0IGEgZGFzaGJvYXJkIHNob3VsZCBiZSBjcmVhdGVkIGFuZCBgcnVudGltZTpzaGlueWAgd2hpY2ggYWxsb3dzIGZvciB0aGUgZGFzaGJvYXJkIHRvIGJlIGludGVyYWN0aXZlOg0KDQpvdXRwdXQ6IA0KICBmbGV4ZGFzaGJvYXJkOjpmbGV4X2Rhc2hib2FyZDoNCiAgICBsb2dvOiBodHRwczovL2ljb25zLmljb25hcmNoaXZlLmNvbS9pY29ucy9pY29uczgvd2luZG93cy04LzQ4L1Byb2dyYW1taW5nLURhc2hib2FyZC1pY29uLnBuZw0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIG9yaWVudGF0aW9uOiBjb2x1bW5zDQogICAgc291cmNlX2NvZGU6IGVtYmVkDQogICAgdmVydGljYWxfbGF5b3V0OiBmaWxsDQpydW50aW1lOiBzaGlueQ0KDQoNCldlIGFsc28gaW50cm9kdWNlZCBhbiBpY29uIGFzIGEgbG9nbywgcHJvdmlkZWQgYSB0aGVtZSB3aXRoIGEgY29sb3Igc2NoZW1lLCBkZWZpbmVkIHRoZSBvcmllbnRhdGlvbiAoYW5kIHRodXMgb3JkZXIpIG9mIGNvZGVkIG91dHB1dCwgYWRkZWQgYSBuYXZpZ2F0aW9uIGJhciBpdGVtIHRvIGdpdmUgdXNlcnMgZWFzeSBhY2Nlc3MgdG8gdGhlIGNvZGUgdXNlZCwgYW5kICBsaW1pdGVkIHNjcm9sbGluZyB3aXRoIHRoZSBgdmVydGljbGVfbGF5b3V0OiBmaWxsYCBvcHRpb24uDQoNCiMjIyAqKjQpKiogRGVzaWduIHRoZSBsYXlvdXQgb2YgdGhlIGRhc2hib2FyZC4NCg0KRGFzaGJvYXJkcyBhcmUgaW5oZXJlbnRseSB2aXN1YWwsIG1ha2luZyB0aGlzIHN0ZXAgdGhlIG1vc3QgdGltZSBpbnRlbnNpdmUgYWZ0ZXIgY29udGVudCBjcmVhdGlvbi4gVG8gZ29hbCBpcyB0byBwcmVzZW50IHRoZSBkYXRhIGluIGEgd2F5IHRoYXQgaXMgYm90aCBtZWFuaW5nZnVsIGFuZCB2aXN1YWxseSBhcHBlYWxpbmcuDQoNCk9uIHRoaXMgZGFzaGJvYXJkLCB3ZSB3YW50ZWQgdG8gcHJlc2VudCBzdGF0aWMgcGxvdHMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgYW5kIG9mIGluZGl2aWR1YWwgc3RhdGVzLiBXZSBhbHNvIHdhbnRlZCB0byBkaXNwbGF5IHRoZSBsb2NhdGlvbnMgb2Ygc2Nob29sIHNob290aW5ncyBhbmQgcHJvdmlkZSBzb21lIGluZm9ybWF0aW9uIGFib3V0IHNjaG9vbCBzaG9vdGluZ3MuIEFzaWRlIGZyb20gYmVpbmcgYSBkYXNoYm9hcmQsIHdlIHdhbnRlZCB0byBjcmVhdGUgYW4gZWR1Y2F0aW9uYWwgcmVzb3VyY2UgdGhhdCB3YXMgcmVwcm9kdWNpYmxlIGZvciBvdGhlcnMuIExhc3RseSwgYXMgdGhpcyBpcyBhIHNlbnNpdGl2ZSB0b3BpYywgd2Ugd2FudGVkIHRvIHJhaXNlIGF3YXJlbmVzcyBhbmQgcHJvdmlkZSBpbmZvcm1hdGlvbiB0aGF0IGNvdWxkIGhlbHAgb3RoZXJzIGFjdC4NCg0KR2l2ZW4gdGhlc2UgZ29hbHMsIHdlIGRlY2lkZWQgb24gdGhlIGZvbGxvd2luZyBwYWdlIGxheW91dDoNCg0KKyBBYm91dA0KKyBUaGUgRGF0YQ0KKyBVUyBTdGF0aXN0aWNzDQorIFN0YXRlIFN0YXRpc3RpY3MNCisgTWFwDQorIFR1dG9yaWFsDQorIEdldCBIZWxwDQoNClRoZSBmaXJzdCBwYWdlIGdpdmVzIHVzZXJzIHRvIHRoZSBvcHBvcnR1bml0eSB0byBsb29rIGF0IHRoZSBkYXRhIHRoZW1zZWx2ZXMuIE1vcmUgY29tcGxpY2F0ZWQgY29tcG9uZW50cyBzdWNoIGFzIHRoZSBtYXAgb2YgZWFjaCBpbmNpZGVudCB3ZXJlIGxlZnQgYWxvbmUgb24gYSBzaW5nbGUgcGFnZS4gVVMgYW5kIHN0YXRlLWxldmVsIHN0YXRpc3RpY3Mgd2VyZSBzZXBhcmF0ZWQgZnJvbSBvbmUgYW5vdGhlci4gVGhpcyBzaG9ydCB0dXRvcmlhbCBvbiBob3cgdG8gY3JlYXRlIHRoZSBkYXNoYm9hcmQgYW5kIHNvdXJjZSBjb2RlIHdlcmUgaW5jbHVkZWQgaW4gdGhlIGRhc2hib2FyZCB3aXRoIHByb2dyYW1tZXJzIGF0IGFsbCBsZXZlbHMgaW4gbWluZC4NCg0KIyMjICoqNSkqKiBBZGQgY29udGVudCB0byB0aGUgZGFzaGJvYXJkLg0KDQpZb3UgY2FuIGJlZ2luIGFkZGluZyBjb250ZW50IHRvIHRoZSBkYXNoYm9hcmQgb25jZSB5b3UgaGF2ZSBhbiBpbml0aWFsIGxheW91dCBpbiBtaW5kLiBLZWVwIGluIG1pbmQgdGhhdCB0aGlzIHdpbGwgbGlrZWx5IGJlIGFuIGl0ZXJhdGl2ZSBwcm9jZXNzLiANCg0KVGhlIFIgTWFya2Rvd24gZmlsZSB1c2VkIHRvIGNyZWF0ZSBhIGRhc2hib2FyZCB3aXRoIGBmbGV4ZGFzaGJvYXJkYCB3b3JrcyBzaW1pbGFybHkgYXMgaXQgZG9lcyBpbiBvdGhlciBjYXNlcywgd2l0aCBhIGZldyBleGNlcHRpb25zLg0KDQpSIGNvZGUgY2h1bmtzIGNhbiBiZSBkZWZpbmVkIGxpa2Ugc286DQoNCg0KJycne3IsIGVjaG8gPSBUUlVFfQ0KIyBDb2RlIGNodW5rcyBjYW4gYmUgZXhwbGljaXRseSBpbmNsdWRlZA0KJycnDQoNCicnJ3tyLCBlY2hvID0gRkFMU0V9DQoNCiMgQ29kZSBjaHVua3MgYXJlIGhpZGRlbiBieSBkZWZhdWx0IA0KJycnDQoNClBhZ2VzIGFuZCBjb2x1bW5zIHdpdGhpbiBwYWdlcyBjYW4gYmUgZGVmaW5lZCBsaWtlIHNvOg0KDQoNClBhZ2UNCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICAgDQoNCkNvbHVtbiB7ZGF0YS13aWR0aD01MDB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkNvbHVtbiB7ZGF0YS13aWR0aD01MDB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KIyMjICoqNikqKiBBZGQgY29udGVudCB0byB0aGUgcGFnZXMgYW5kIGNvbHVtbnMuDQoNClBsb3RzIGFuZCBvdGhlciBlbGVtZW50cyBjYW4gYmUgYWRkZWQgd2l0aGluIGNvbHVtbnMgbGlrZSBzbzoNCg0KIyMjIFBsb3QgbmFtZQ0KDQonJyd7cn0NCiMgaW5jbHVkZSBwbG90IGNvZGUgaGVyZQ0KJycnDQoNClZhbHVlIEJveGVzLCB3aGljaCBhcmUgZXNzZW50aWFsbHkgdGV4dCBib3hlcywgY2FuIGJlIGRlZmluZWQgbGlrZSBzbzoNCg0KDQojIyMgVmFsdWVCb3hUZXh0DQoNCicnJ3tyfQ0KdmFsdWVCb3godmFsdWUgPSAxMA0KICBjb2xvciA9ICJ3aGl0ZSIpDQoNCg0KDQpHYXVnZXMsIGNhbiBiZSBkZWZpbmVkIGxpa2Ugc286DQoNCiMjIyBHYXVnZVRleHQNCg0KJycne3J9DQpmbGV4ZGFzaGJvYXJkOjpnYXVnZSh2YWx1ZSA9IDEwLCANCiAgICAgICAgICAgICAgICAgICAgICAgbWluID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgIG1heCA9IDEwMCwgDQogICAgICAgICAgICAgICAgICAgIHN5bWJvbCA9ICIlIikNCg0KJycnDQoNCiMjIyMNCldoaWNoIHdpbGwgcHJvZHVjZSBvdXRwdXQgbGlrZSB0aGlzOg0KJycne3IsIG91dC53aWR0aD0gIjQwJSIsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnYXVnZV9vdXRwdXQucG5nIikpDQonJycNCg0KIyMjIEFkZGl0aW9uYWwgSW5mbw0KQXMgbWVudGlvbmVkIGJlZm9yZSwgdGhlIGBmbGV4ZGFzaGJvYXJkYCBtZXRhZGF0YSBpbmNsdWRlZCBpbiB0aGUgYFlBTUxgIGFsc28gYWx0ZXJzIGhvdyBSIE1hcmtkb3duIGRvY3VtZW50cyBhcmUgcmVuZGVyZWQuIEZvciBtb3JlIG9uIGhvdyB5b3UgY2FuIGxldmVyYWdlIGJvdGggdGhlIGBSTWFya2Rvd25gIHBhY2thZ2UgYW5kIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZSB0byBwcm9kdWNlIGEgZGFzaGJvYXJkLCBjbGljayBbaGVyZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZmxleGRhc2hib2FyZC9pbmRleC5odG1sKS4NCg0KVGhpcyBbc3VwcGxlbWVudGFyeSByZXNvdXJjZSBieSB0aGUgT3BlbiBDYXNlIFN0dWRpZXMgcHJvamVjdF0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1zY2hvb2wtc2hvb3RpbmdzLWRhc2hib2FyZC8pIHByb3ZpZGVzIGEgY2FzZSBzdHVkeSBvbiBob3cgdG8gY3JlYXRlIHRoaXMgdmVyeSBkYXNoYm9hcmQgaW4gbW9yZSBkZXRhaWwuDQoNCmBgYA0KDQo8L2RldGFpbHM+DQoNCioqKg0KDQojIyAqKlRoZSBHZXQgSGVscCBwYWdlKioNCioqKg0KDQpXZSBjcmVhdGUgYSAqKkdldCBIZWxwKiogcGFnZSB0byBzcHJlYWQgYXdhcmVuZXNzIG9uIHRoaXMgaW1wb3J0YW50IHB1YmxpYyBoZWFsdGggdG9waWMuDQoNCiMjIyAqKkxvb2sqKg0KKioqDQoNClRoaXMgaXMgd2hhdCB0aGUgcGFnZSB3aWxsIGxvb2sgbGlrZToNCg0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciICwgIlRoZV9HZXRfSGVscF9QYWdlLnBuZyIpKQ0KYGBgDQoNCg0KIyMjICoqT3ZlcmFsbCBTdHJ1Y3R1cmUqKg0KKioqDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiAsICJUaGVfR2V0X0hlbHBfUGFnZV9zdHJ1Y3R1cmUucG5nIikpDQpgYGANCg0KDQojIyMgKipEZXRhaWxzKioNCioqKg0KDQpUaGlzIHBhZ2UgaGFzIHR3byBjb2x1bW5zLiBUaGUgZmlyc3QgY29sdW1uIGlzIG11Y2ggd2lkZXIgdGhhbiB0aGUgc2Vjb25kLiBUaGlzIGZpcnN0IGNvbHVtbiBpbmNsdWRlcyB0d28gY29sb3JlZCBiYWNrZ3JvdW5kcyB3aGljaCB3ZXJlIGNyZWF0ZWQgdXNpbmcgW0NTU10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQ1NTKSBjb2RlLiBTZWUgW1RoZSBBYm91dCBQYWdlXSBEZXRhaWxzIHNlY3Rpb24gdG8gZm9yIG1vcmUgZGV0YWlscyBhYm91dCBob3cgdGhpcyB3b3Jrcy4gDQoNClRoZSB0ZXh0IHRoYXQgd2Ugd2FudCBhbHRlcmVkIHdpdGggdGhpcyBwYXJ0aWN1bGFyIHN0eWxlIGlzIGRlbGluZWF0ZWQgYnkgdGhlIDxkaXY+IHRvIHN0YXJ0IGFuZCB0aGUgPC9kaXY+IHRvIGVuZCB0aGUgc3R5bGUuDQoNClRoZSBpbnN0cnVjdGlvbnMgZm9yIHRoZSBzdHlsZSBhcmUgd2l0aGluIHRoZSA8c3R5bGU+IGFuZCA8L3N0eWxlPiBjb250ZW50IGRpdmlkZXJzLiANCg0KSW5zaWRlIHRoZXNlIGRpdmlkZXJzIGlzIENTUyBjb2RlLCB3aGljaCBpcyB3aGF0IGlzIHVzZWQgdG8gc3R5bGl6ZSBIVE1MLiBUaGUgZGl2LmJsdWUgaXMgdGhlIG5hbWUgb2YgdGhlIGZpcnN0IHBhcnRpY3VsYXIgc3R5bGUgd2hpY2ggaW52b2x2ZXMgYSBwYXJ0aWN1bGFyIGJhY2tncm91bmQgY29sb3IgKCNlNmYwZmZGKSAoc2VlIFtoZXJlXShodHRwczovL3d3dy53M3NjaG9vbHMuY29tL2Nzc3JlZi9jc3NfY29sb3JzLmFzcCkgZm9yIG1vcmUgY29sb3Igb3B0aW9ucyksIHdpdGggYSBib2FyZGVyIHJhZGl1cyBvZiA1IHBpeGVscyB0byByb3VuZCB0aGUgZWRnZXMgb2YgdGhlIGJhY2tncm91bmQgY29sb3IgYXJvdW5kIHRoZSB0ZXh0IHdpdGggYSBzaXplIDUgcGl4ZWwgcmFkaXVzLiBUaGUgY29kZSBhbHNvIHN0YXRlcyB0aGF0IGEgW3BhZGRpbmddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0NTUy9wYWRkaW5nKSBzcGVjaWZpY2F0aW9uIGZvciB0aGUgc2l6ZSBvZiB0aGUgbWFyZ2lucyBvZiB0aGUgdGV4dCBib3ggYXJvdW5kIHRoZSB0ZXh0IGFuZCBpdCBzcGVjaWZpZXMgdGhhdCBmb250IHNob3VsZCBiZSBvZiAyMCBwaXhlbHMuDQoNClRoZSBkaXYuYmx1ZSBzcGVjaWZpZXMgdGhhdCBibHVlIGlzIHRoZSBuYW1lIG9mIHRoaXMgc3R5bGUsIHRodXMgd2UgY2FuIHRoZW4gdXNlIDxkaXYgY2xhc3MgPSBibHVlPiAoY2FsbGVkIGEgW0NTUyBzZWxlY3Rvcl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9HbG9zc2FyeS9DU1NfU2VsZWN0b3IpKSB0byBzdHlsZSB0aGUgdGV4dCB0aGlzIHdheS4gVGhpcyBjYW4gdGhlbiBiZSB1c2VkIGFnYWluIGFueSB0aW1lIHdlIHdhbnQgdGhpcyBzdHlsZSBsaWtlIHNvOg0KDQo8ZGl2IGNsYXNzID0gImJsdWUiPg0KDQp0ZXh0IA0KDQo8L2Rpdj4NCg0KU2VlIHRoaXMgW3dlYnNpdGVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUTUwvRWxlbWVudC9kaXYpIHRvIGxlYXJuIG1vcmUgYWJvdXQgSFRNTCBhbmQgQ1NTLg0KDQoNCldlIGFsc28gc2VlIGEgbGlzdCBvbiB0aGlzIHBhZ2UsIGArYCBzaWducyBhcmUgdXNlZCB0byBpbmRpY2F0ZSBuZXcgaXRlbXMuIEltcG9ydGFudGx5IHR3byBzcGFjZXMgYXJlIG5lY2Vzc2FyeSBhZnRlciBlYWNoIGl0ZW0gdG8gc3RhcnQgYSBuZXcgbGluZS4gDQoNClRoZSBvdGhlciB1bmlxdWUgYXNwZWN0IGFib3V0IHRoaXMgcGFnZSBhcmUgdGhlIHRlbGVwaG9uZSBsaW5rcyBsaWtlIHNvIGBbKzEtODQ0LTUtU0FZTk9XXSh0ZWw6MTg0NDU3Mjk2NjkpYC4NCg0KQnkgdXNpbmcgYHRlbDpgIGFuZCB0aGUgbnVtYmVyLCB1c2VycyBjYW4gY2xpY2sgdGhpcyBsaW5rIHRvIGRpcmVjdGx5IGNhbGwgdGhlIHRlbGVwaG9uZSBudW1iZXIgZnJvbSB0aGVpciBjb21wdXRlciBvciBwaG9uZSBpZiB0aGVpciBkZXZpY2UgaGFzIHN1Y2ggY2FwYWJpbGl0aWVzLiANCg0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgdGhlIGNvZGUgZm9yIHRoaXMgcGFnZS4gPC9zdW1tYXJ5Pg0KDQpgYGANCkdldCBIZWxwIHtkYXRhLWljb249ImZhLWV4Y2xhbWF0aW9uLXRyaWFuZ2xlIn0NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICAgDQoNCkNvbHVtbiB7ZGF0YS13aWR0aD04MDB9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIw0KDQoqKldhcm5pbmcgU2lnbnMqKg0KDQpGcm9tIFtTYW5keSBIb29rIFByb21pc2VdKGh0dHBzOi8vd3d3LnNhbmR5aG9va3Byb21pc2Uub3JnL2d1bi12aW9sZW5jZS9rbm93LXRoZS1zaWducy1vZi1ndW4tdmlvbGVuY2UvKS4uLg0KDQo8c3R5bGU+DQpkaXYuYmx1ZSB7IGJhY2tncm91bmQtY29sb3I6I2U2ZjBmZjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30NCjwvc3R5bGU+DQo8ZGl2IGNsYXNzID0gImJsdWUiPg0KDQpIZXJlIGlzIGEgbGlzdCBvZiBwb3RlbnRpYWwgd2FybmluZyBzaWducyB0aGF0IGNhbiBzaWduYWwgYW4gaW5kaXZpZHVhbCBtYXkgYmUgaW4gY3Jpc2lzIGFuZC9vciBuZWVkIGhlbHA6DQoNCisgU3VkZGVubHkgd2l0aGRyYXdpbmcgZnJvbSBwZW9wbGUgYW5kIGFjdGl2aXRpZXMNCisgQ29uc2lzdGVudCBidWxseWluZyBvciBpbnRpbWlkYXRpbmcgb3RoZXJzLCBvciBiZWluZyBidWxsaWVkIGJ5IG90aGVycw0KKyBFeHRyZW1lIG1vb2Qgb3IgcGVyc29uYWxpdHkgY2hhbmdlcw0KKyBWaWN0aW0gb2YgY29uc3RhbnQgc29jaWFsIHJlamVjdGlvbg0KKyBUYWxraW5nIGFib3V0IHBsYW5zIG9yIGFjdGl2ZWx5IG1ha2luZyBwbGFucyB0byBoYXJtIHRoZW1zZWx2ZXMgb3Igb3RoZXJzDQorIEJyaW5naW5nIGEgd2VhcG9uIHRvIHNjaG9vbCDigJMgb3IgdGhyZWF0ZW5pbmcgb3IgdGFsa2luZyBhYm91dCBkb2luZyBzbw0KKyBCcmFnZ2luZyBhYm91dCBvciB3YXJuaW5nIG90aGVycyBhYm91dCBhbiB1cGNvbWluZyBhY3Qgb2YgdmlvbGVuY2UNCisgUmVjcnVpdGluZyBvdGhlcnMgdG8gam9pbiBpbiBhIHBsYW5uZWQgYWN0IG9mIHZpb2xlbmNlDQorIFdhcm5pbmcgc3R1ZGVudHMgdG8gc3RheSBhd2F5IGZyb20gc2Nob29sIG9yIGV2ZW50cw0KKyBFeHByZXNzaW5nIGZhc2NpbmF0aW9uIHdpdGggZ3VucyBhbmQvb3Igc2Nob29sIHNob290aW5ncw0KKyBFeHByZXNzaW5nIGhvcGVsZXNzbmVzcyBhYm91dCB0aGUgZnV0dXJlDQorIEV4dHJlbWUsIHByb2xvbmdlZCBzYWRuZXNzIG9yIGRpc3RyZXNzDQorIEV4cHJlc3Npbmcgb3Igc2hvd2luZyBmZWVsaW5ncyBvZiBpc29sYXRpb24NCisgQnJhZ2dpbmcgYWJvdXQgYWNjZXNzIHRvIGd1bnMNCg0KKipUaGlzIGxpc3QgaXMgbm90IGEgY29tcHJlaGVuc2l2ZSBsaXN0IG9mIHdhcm5pbmcgc2lnbnMgbm9yIGRvZXMgZXhoaWJpdGluZyBvbmUgb2YgdGhlc2Ugc2lnbnMgaW5kaWNhdGUgaW1taW5lbnQgdmlvbGVuY2UuKioNCg0KQWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgYXJ0aWNsZToNCg0KRmxhbm5lcnksIEQuIEouLCBNb2R6ZWxlc2tpLCBXLiAmIEtyZXRzY2htYXIsIEouIE0uIFZpb2xlbmNlIGFuZCBTY2hvb2wgU2hvb3RpbmdzLiBDdXJyIFBzeWNoaWF0cnkgUmVwIDE1LCAzMzEgKDIwMTMpLiBET0k6IFsxMC4xMDA3L3MxMTkyMC0wMTItMDMzMS02XShodHRwczovL2RvaS5vcmcvMTAuMTAwNy9zMTE5MjAtMDEyLTAzMzEtNikNCg0KIlRvIGRhdGUsIHN0dWRpZXMgb2Ygc2Nob29sIHNob290aW5ncyBoYXZlIGNvbmNsdWRlZCB0aGF0IG5vDQpjb25zaXN0ZW50IGFuZCByZWxpYWJsZSBwcm9maWxlIG9mIHNjaG9vbCBzaG9vdGVycyBleGlzdCwgYW5kDQptb3N0IHJlc2VhcmNoZXJzIGFuZCBjbGluaWNpYW5zIHdvdWxkIGFncmVlIHRoYXQgcHJlZGljdGluZw0KdmlvbGVudCBiZWhhdmlvciBpcyBhIHNsaXBwZXJ5IHNsb3BlIHRoYXQgd2lsbCB1c3VhbGx5IHJlc3VsdCBpbg0KbW9yZSBmYWxzZSBwb3NpdGl2ZXMgdGhhbiBmYWxzZSBuZWdhdGl2ZXMuIg0KDQoiLi4ubW9zdCBzaG9vdGVycyB3ZXJlIGRlcHJlc3NlZCwgaGFkIGV4cGVyaWVuY2VkIHNvbWUgc2lnbmlmaWNhbnQNCmxvc3MsIGZlbHQgcGVyc2VjdXRlZCBvciBidWxsaWVkIGJ5IG90aGVycywgYW5kIGhhZCBwcmlvcg0KZGlmZmljdWx0eSBjb3Bpbmcgb3IgaGFkIHByZXZpb3VzbHkgdHJpZWQgc3VpY2lkZS4gTW9zdCBvZg0KdGhlIHNob290ZXJzIGRpZCBub3QsIGhvd2V2ZXIsIGhhdmUgYSBoaXN0b3J5IG9mIGRydWcgYWJ1c2UNCm9yIHZpb2xlbmNlIG9yIGNydWVsdHkgdG8gYW5pbWFscywgY29tbW9uIHBzeWNoaWF0cmljIGluZGljYXRvcnMgb2Ygcmlzaywgbm9yIGRpZCB0aGV5IHJlcG9ydCBleGNlc3NpdmUgZXhwb3N1cmUgdG8NCnZpb2xlbmNlIGluIHRoZSBtZWRpYSAodGhvdWdoIG1hbnkgcHJvZHVjZWQgdGhlaXIgb3duDQp2aW9sZW50IHRoZW1lcyBpbiB3cml0aW5ncyBvciBkcmF3aW5ncykuIg0KDQo8L2Rpdj4NCg0KDQo8c3R5bGU+DQpkaXYucmVkIHsgYmFja2dyb3VuZC1jb2xvcjojQkM4RjhGOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAicmVkIj4NCg0KQWNjb3JkaW5nIHRvIHRoZSBbTmF0aW9uYWwgSW5zdGl0dXRlIG9mIE1lbnRhbCBIZWFsdGggKE5JTUgpXShodHRwczovL3d3dy5uaW1oLm5paC5nb3YvaGVhbHRoL3B1YmxpY2F0aW9ucy90ZWVuLWRlcHJlc3Npb24vaW5kZXguc2h0bWwpe3RhcmdldD0iX2JsYW5rIn06DQoNCkZvciB5b3V0aHMgd2hvIG1heSBiZSBhdCByaXNrIGZvciBzdWljaWRhbCBiZWhhdmlvciwgdmlzaXQgdGhlICoqTmF0aW9uYWwgU3VpY2lkZSBQcmV2ZW50aW9uIExpZmVsaW5lIChOU1BMKSoqIHdlYnNpdGUgYXQgW3d3dy5zdWljaWRlcHJldmVudGlvbmxpZmVsaW5lLm9yZ10od3d3LnN1aWNpZGVwcmV2ZW50aW9ubGlmZWxpbmUub3JnKXt0YXJnZXQ9Il9ibGFuayJ9Lg0KDQpBZGRpdGlvbmFsbHksIHRoZSAqKkNyaXNpcyBUZXh0IExpbmUqKiBpcyBhbm90aGVyIGZyZWUsIGNvbmZpZGVudGlhbCByZXNvdXJjZSBhdmFpbGFibGUgMjQgaG91cnMgYSBkYXksIHNldmVuIGRheXMgYSB3ZWVrLiBWaXNpdCBbd3d3LmNyaXNpc3RleHRsaW5lLm9yZ10od3d3LmNyaXNpc3RleHRsaW5lLm9yZyl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCg0KQWxzbyBzZWUgW2hlcmVdKGh0dHBzOi8vd3d3Lm1oYW5hdGlvbmFsLm9yZy9kZXByZXNzaW9uLXRlZW5zLTApe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgaG93IHRvIHJlY29nbml6ZSBhbmQgaGVscCB5b3V0aHMgZXhwZXJpZW5jaW5nIHN5bXB0b21zIG9mIGRlcHJlc3Npb24gYW5kIHdhcm5pbmcgc2lnbnMgb2Ygc3VpY2lkZS4NCg0KPC9kaXY+DQoNCg0KQ29sdW1uIHtkYXRhLXdpZHRoPTIwMH0NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIA0KDQoqKlJlc3BvbmQgdG8gV2FybmluZyBTaWducyoqDQoNCldoZW4gY29uY2VybmVkIGFib3V0IHRyb3VibGluZyBiZWhhdmlvcnMsIHRlbGwgYSB0cnVzdGVkIGFkdWx0Lg0KDQoNCkNhbGwgKio5MTEqKiBpZiB5b3UgZmVlbCB0aGVyZSBpcyBhbiBpbW1lZGlhdGUgdGhyZWF0LiANCg0KQ2FsbCBbKzEtODQ0LTUtU0FZTk9XXSh0ZWw6MTg0NDU3Mjk2NjkpIGlmIHlvdSB3b3VsZCBsaWtlIHRvIHN1Ym1pdCBhbiBhbm9ueW1vdXMgc2FmZXR5IGNvbmNlcm4uDQoNClRleHQg4oCcSE9NReKAnSB0byAqKjc0MTc0MSoqIHRvIHRleHQgYSB0cmFpbmVkIGNyaXNpcyBjb3Vuc2Vsb3IgMjQgaG91cnMgYSBkYXkuDQoNClRoZSAqKk5hdGlvbmFsIFN1aWNpZGUgUHJldmVudGlvbiBMaWZlbGluZSAoTlNQTCkqKiBpcyBhdmFpbGFibGUgMjQgaG91cnMgYSBkYXksIGV2ZXJ5IGRheSBhdCAqKlsxLTgwMC0yNzMtVEFMSyAoODI1NSldKHRlbDoxODAwMjczODI1NSkqKi4gDQoNClRoZSBkZWFmIGFuZCBoYXJkIG9mIGhlYXJpbmcgY2FuIGNvbnRhY3QgdGhlICoqKE5TUEwpKiogdmlhIFRUWSBhdCAqKlsxLTgwMC03OTktNDg4OV0odGVsOjE4MDA3OTk0ODg5KSoqLiBBbGwgY2FsbHMgYXJlIGNvbmZpZGVudGlhbC4NCmBgYA0KDQo8L2RldGFpbHM+DQoNCioqKg0KDQojICoqU3VtbWFyeSoqDQoqKiogDQoNCiMjICoqU3lub3BzaXMqKg0KKioqDQoNCkluIHRoaXMgY2FzZSBzdHVkeSwgd2UgZGVtb25zdHJhdGVkIHRoZSBiYXNpY3Mgb2YgUiBNYXJrZG93biBhbmQgaG93IHRvIGNyZWF0ZSBhIGRhc2hib2FyZCB3aXRoIHVzaW5nIHRoZSBgZmxleGRhc2hib2FyZGAgcGFja2FnZS4gV2UgYWxzbyBkZW1vbnN0cmF0ZWQgaG93IHRvIGluY2x1ZGUgYW4gaW50ZXJhY3RpdmUgdGFibGUgd2l0aCB0aGUgYERUYCBwYWNrYWdlLCBob3cgdG8gaW5jbHVkZSBpbnRlcmFjdGl2ZSBwbG90cyB1c2luZyBmdW5jdGlvbnMgb2YgdGhlIGBzaGlueWAgcGFja2FnZSBzdWNoIGFzIGByZW5kZXJQbG90KClgLiBXZSBpbmNsdWRlZCBpbnRlcmFjdGl2ZSB2YWx1ZSBib3hlcyB1c2luZyB0aGUgYHJlbmRlclZhbHVlQm94KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBmbGV4ZGFzaGJvYXJkYCBwYWNrYWdlLCB3aGljaCB3b3JrcyB3aXRoIHRoZSBgc2hpbnlgIHBhY2thZ2UuIEZpbmFsbHksIHdlIHNob3dlZCBob3cgdG8gaW5jbHVkZSBpbnRlcmFjdGl2ZSBtYXBzIHVzaW5nIHRoZSBgbGVhZmxldGAgcGFja2FnZS4gDQoNClRoaXMgY2FzZSBzdHVkeSBhbHNvIGV4cGxvcmVkIGhvdyB0byBwcm9wZXJseSBjYWxjdWxhdGUgYW5kIGludGVycHJldCBwZXJjZW50YWdlcyB3aGVuIHRoZSBkYXRhIGhhcyBtaXNzaW5nIHZhbHVlcy4gV2UgYWxzbyBkaXNjdXNzZWQgdGhlIGJlbmVmaXRzIGFuZCBsaW1pdGluZyBhc3BlY3RzIG9mIHBpZSBjaGFydHMgKHVzaW5nIHRoZSBgZ2dwbG90MmAgcGFja2FnZSkgYW5kIHdhZmZsZSBwbG90cyAodXNpbmcgdGhlIGB3YWZmbGVgIHBhY2thZ2UpLg0KDQpPdmVyYWxsLCB0aGUgZGFzaGJvYXJkIHdlIGNyZWF0ZWQgd2hpY2ggY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3JzY29ubmVjdC5iaW9zdGF0Lmpoc3BoLmVkdS9vY3MtYnAtc2Nob29sLXNob290aW5ncy1kYXNoYm9hcmQvKSwgDQpzaG93cyB0aGF0IHRoZSBudW1iZXIgb2Ygc2Nob29sIHNob290aW5ncyBwZXIgeWVhciBoYXMgaW5jcmVhc2VkIG92ZXJ0aW1lLiBGdXJ0aGVyIGludmVzdGlnYXRpb24gaXMgbmVjZXNzYXJ5IHRvIGRldGVybWluZSBpZiB0aGlzIGlzIHNpbXBseSBkdWUgdG8gaW5jcmVhc2VzIGluIHBvcHVsYXRpb24gYWxvbmUgb3IgaWYgdGhlIHJhdGUgaGFzIGluY3JlYXNlZCBkdWUgdG8gb3RoZXIgZmFjdG9ycyBhbmQgaWYgc28sIHdoYXQgdGhvc2UgZmFjdG9ycyBtaWdodCBiZS4gSXQgaXMgYWxzbyBjbGVhciB0aGF0IHRoZSBudW1iZXIgb2Ygc2Nob29sIHNob290aW5ncyBhbmQgdGhlIG51bWJlciBvZiBkZWF0aHMgcGVyIGNhcGl0YSB2YXJpZXMgYnkgc3RhdGUuIFRoZXJlIGFwcGVhcnMgdG8gYmUgb3RoZXIgYXNwZWN0cyBhY2NvdW50aW5nIGZvciBzdGF0ZSBkaWZmZXJlbmNlcy4gDQoNCipOb3RlIHRoZSBsaW1pdGF0aW9ucyBvZiB0aGUgZGFzaGJvYXJkIGluIHRoZSBbTGltaXRhdGlvbnNdKGh0dHBzOi8vd3d3Lm9wZW5jYXNlc3R1ZGllcy5vcmcvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkLyNMaW1pdGF0aW9ucykgc2VjdGlvbi4qDQoNCiMgKipTdWdnZXN0ZWQgSG9tZXdvcmsqKg0KKioqIA0KDQpDcmVhdGUgYW5vdGhlciBkYXNoYm9hcmQgd2l0aCBncmFwaHMgYW5kIHN0YXRpc3RpY3MgZmVhdHVyaW5nIG90aGVyIGVsZW1lbnRzIHdpdGhpbiB0aGlzIGRhdGFzZXQuIEZvciBleGFtcGxlLCBzdHVkZW50cyBtYXkgY3JlYXRlIGdyYXBocyB0aGF0IGV4cGxvcmUgd2hhdCBzY2hvb2wgZXZlbnRzIGFyZSByZXBvcnRlZCB0byBoYXZlIG1vcmUgc2Nob29sIHNob290aW5ncy4NCg0KDQojICoqQWRkaXRpb25hbCBJbmZvcm1hdGlvbioqDQoqKioNCg0KIyMgKipIZWxwZnVsIExpbmtzKioNCioqKg0KDQpUaGUgbGluayB0byB0aGUgZGFzaGJvYXJkIGRlc2NyaWJlZCBpbiB0aGlzIGNhc2Ugc3R1ZHkgY2FuIGJlIGZvdW5kICBbaGVyZV0oaHR0cHM6Ly9yc2Nvbm5lY3QuYmlvc3RhdC5qaHNwaC5lZHUvb2NzLWJwLXNjaG9vbC1zaG9vdGluZ3MtZGFzaGJvYXJkLykuDQoNCg0KW1JTdHVkaW9dKGh0dHBzOi8vcnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9mZWF0dXJlcy8pe3RhcmdldD0iX2JsYW5rIn0gIA0KW0NoZWF0c2hlZXQgb24gUlN0dWlkbyBJREVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvcnN0dWRpby1pZGUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9ICANCltPdGhlciBSU3R1ZGlvIGNoZWF0c2hlZXRzXShodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pe3RhcmdldD0iX2JsYW5rIn0gICANCltSU3R1ZGlvIHByb2plY3RzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3dvcmtmbG93LXByb2plY3RzLmh0bWwpDQoNCltUaWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQoNCltQaXBpbmcgaW4gUl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ3JpdHRyL3ZpZ25ldHRlcy9tYWdyaXR0ci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQoNCltTdHJpbmcgbWFuaXB1bGF0aW9uIGNoZWF0c2hlZXRdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbVGFibGUgZm9ybWF0c10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2lkZV9hbmRfbmFycm93X2RhdGEpe3RhcmdldD0iX2JsYW5rIn0NCg0KW0dlb2NvZGluZ10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvY29kaW5nKSAgDQpbQ29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtIChDUlMpXShodHRwczovL3d3dy53My5vcmcvMjAxNS9zcGF0aWFsL3dpa2kvQ29vcmRpbmF0ZV9SZWZlcmVuY2VfU3lzdGVtcykgW0VTUEddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0VQU0dfR2VvZGV0aWNfUGFyYW1ldGVyX0RhdGFzZXQpDQpbV29ybGQgR2VvZGV0aWMgU3lzdGVtIChXR1MpIHZlcnNpb24gODQgYWxzbyBjYWxsZWQgRVNQRzo0MzI2IF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV29ybGRfR2VvZGV0aWNfU3lzdGVtI1dHUzg0KSAgIA0KW0FsYmVycyBlcXVhbC1hcmVhIGNvbmljIHByb2plY3Rpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0FsYmVyc19wcm9qZWN0aW9uIzp+OnRleHQ9VGhlJTIwQWxiZXJzJTIwZXF1YWwlMkRhcmVhJTIwY29uaWMsdGhhdCUyMHVzZXMlMjB0d28lMjBzdGFuZGFyZCUyMHBhcmFsbGVscy4mdGV4dD1UaGUlMjBBbGJlcnMlMjBwcm9qZWN0aW9uJTIwaXMlMjB1c2VkLHRoZSUyMFVuaXRlZCUyMFN0YXRlcyUyMENlbnN1cyUyMEJ1cmVhdS4pICAgDQpbY3JzIDEwMjAwOF0oaHR0cHM6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy9yZWYvZXNyaS8xMDIwMDgvaHRtbC8pICANCg0KVG8gbGVhcm4gbW9yZSBhYm91dCBnZW9zcGF0aWFsIGNvb3JkaW5hdGUgc3lzdGVtcyBzZWUgW2hlcmVdKGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L3NpdGVzL2RlZmF1bHQvZmlsZXMvMjAyMC0wNC9PdmVydmlld0Nvb3JkaW5hdGVSZWZlcmVuY2VTeXN0ZW1zLnBkZikgYW5kIFtoZXJlXShodHRwczovL2d1aWRlcy5saWJyYXJ5LmR1a2UuZWR1L3ItZ2Vvc3BhdGlhbC9DUlMpLg0KDQoNCltgZ2dwbG90MmAgcGFja2FnZV0oaHR0cDovL2dncGxvdDIudGlkeXZlcnNlLm9yZyl7dGFyZ2V0PSJfYmxhbmsifSAgICANClBsZWFzZSBzZWUgW3RoaXMgY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1jbzItZW1pc3Npb25zLykgIGZvciBtb3JlIGRldGFpbHMgb24gdXNpbmcgYGdncGxvdDJgICAgIA0KW2dyYW1tYXIgb2YgZ3JhcGhpY3NdKGh0dHA6Ly92aXRhLmhhZC5jby5uei9wYXBlcnMvbGF5ZXJlZC1ncmFtbWFyLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICANCltgZ2dwbG90MmAgdGhlbWVzXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2d0aGVtZS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQoNCltNb3RpdmF0aW5nIGFydGljbGUgZm9yIHRoaXMgY2FzZSBzdHVkeSBhYm91dCBzY2hvb2wgc2hvb3RpbmdzXShodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2NvbnRlbnQvcGRmLzEwLjEwMDcvczExOTIwLTAxMi0wMzMxLTYucGRmKQ0KDQpBbHNvIHNlZSB0aGlzIFthcnRpY2xlXShodHRwczovL3NpZXByLnN0YW5mb3JkLmVkdS9zaXRlcy9kZWZhdWx0L2ZpbGVzL3B1YmxpY2F0aW9ucy8xOS0wMzYucGRmKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBpbXBhY3RzIG9mIHNjaG9vbCBzaG9vdGluZ3MuDQoNCg0KW0xpZ2h0d2VpZ2h0IG1hcmt1cCBsYW5ndWFnZXMoTE1MKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlnaHR3ZWlnaHRfbWFya3VwX2xhbmd1YWdlKSAgDQpbTWFya2Rvd25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01hcmtkb3duKSAgDQpbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pICAgDQpbYGtuaXRyYF0oaHR0cHM6Ly95aWh1aS5vcmcva25pdHIvKSAgDQpbYHJtYXJrZG93bmAgKHBhY2thZ2UpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcm1hcmtkb3duL3JtYXJrZG93bi5wZGYpDQoNClNlZSB0aGlzIFtib29rXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB3b3JraW5nIHdpdGggUiBNYXJrZG93biBmaWxlcy4gDQoNClRoZSBSU3R1ZGlvIFtjaGVhdHNoZWV0IGZvciBSIE1hcmtkb3duXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9yYXcvbWFzdGVyL3JtYXJrZG93bi0yLjAucGRmKSBhbmQgdGhpcyBbdHV0b3JpYWxdKGh0dHBzOi8vb3VyY29kaW5nY2x1Yi5naXRodWIuaW8vdHV0b3JpYWxzL3JtYXJrZG93bi8pIGFyZSBncmVhdCBmb3IgZ2V0dGluZyBzdGFydGVkLiANCg0KW1BhbmRvY10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUGFuZG9jKSAgDQoNCltZQU1MXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9ZQU1MKSAgDQpbQ29uZmlndXJhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29uZmlndXJhdGlvbl9maWxlKSAgDQoNCltmbGV4ZGFzaGJvYXJkXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkLykgIA0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL2ludHJvZHVjaW5nLWZsZXhkYXNoYm9hcmRzLykgZm9yIGEgdmlkZW8gYWJvdXQgZmxleGRhc2hib2FyZCBhbmQgW2hlcmVdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvKSBmb3IgYSBtb3JlIGluZm9ybWF0aW9uIG9uIGhvdyB0byB1c2UgdGhpcyBwYWNrYWdlLiAgIA0KU2VlIFtoZXJlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkL3VzaW5nLmh0bWwjY29tcG9uZW50cykgZm9yIGEgbGlzdCBvZiBvdGhlciBwYWNrYWdlcyB0aGF0IGFyZSB1c2VmdWwgZm9yIGFkZGluZyBlbGVtZW50cyB0byBkYXNoYm9hcmRzIGNyZWF0ZWQgd2l0aCB0aGUgYGZsZXhkYXNoYm9hcmRgIHBhY2thZ2UuICAgDQpTZWUgW2hlcmVdKGh0dHBzOi8vd3d3LmRhdGFkcmVhbWluZy5vcmcvcG9zdC9yLW1hcmtkb3duLXRoZW1lLWdhbGxlcnkvKSBmb3IgYSBsaXN0IG9mIFIgTWFya2Rvd24gdGhlbWVzIHdoaWNoIGNhbiBiZSB1c2VkIHdpdGggYGZsZXhkYXNoYmFyZGAuICAgDQpTZWUgW0ZvbnQgQXdlc29tZV0oaHR0cHM6Ly9mb250YXdlc29tZS5jb20vaWNvbnM/ZD1nYWxsZXJ5KSBmb3IgaWNvbnMuICANCg0KVG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBgc2hpbnlgIHdpdGggdGhlIGBmbGV4ZGFzaGJvYXJkYCBwYWNrYWdlIHRvIGNyZWF0ZSBpbnRlcmFjdGl2ZSBkYXNoYm9hcmRzLCBzZWUgdGhpcyBbdHV0b3JpYWxdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2ZsZXhkYXNoYm9hcmQvc2hpbnkuaHRtbCkuICAgDQoNCltsZWFmbGV0IChSIHBhY2thZ2UpXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvKSAgIA0KW0xlYWZsZXQgKEphdmFTY3JpcHQgTGlicmFyeSldKGh0dHBzOi8vbGVhZmxldGpzLmNvbS8pICAgDQoNCltzaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pICANClNlZSBbaGVyZV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykgZm9yIGEgZ2FsbGVyeSBvZiBgc2hpbnlgIGV4YW1wbGVzLg0KDQpTZWUgdGhpcyBbd2Vic2l0ZV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9zaGlueWRhc2hib2FyZC8pIHRvIGxlYXJuIGFib3V0IGEgbW9yZSBmbGV4aWJsZSBhbmQgc2xpZ2h0bHkgbW9yZSBjaGFsbGVuZ2luZyBvcHRpb24gZm9yIGNyZWF0aW5nIGRhc2hib2FyZHMgaW4gUiB1c2luZyBhIHBhY2thZ2UgY2FsbGVkIGBzaGlueWRhc2hib2FyZGAuDQoNCg0KPHU+KipQYWNrYWdlcyB1c2VkIGluIHRoaXMgY2FzZSBzdHVkeToqKiA8L3U+DQoNClBhY2thZ2UgICB8IFVzZSBpbiB0aGlzIGNhc2Ugc3R1ZHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tDQpbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgIHwgdG8gZWFzaWx5IGxvYWQgYW5kIHNhdmUgZGF0YSAgDQpbcmVhZHJdKGh0dHBzOi8vcmVhZHIudGlkeXZlcnNlLm9yZy8pIHwgIHRvIGltcG9ydCB0aGUgZGF0YSAgYXMgYSBjc3YgZmlsZSAgDQpbZ29vZ2xlc2hlZXRzNF0oaHR0cHM6Ly9nb29nbGVzaGVldHM0LnRpZHl2ZXJzZS5vcmcvKSB8IHRvIGltcG9ydCBkaXJlY3RseSBmcm9tIEdvb2dsZSBTaGVldHMNClt0aWJibGVdKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcvKSB8IHRvIGNyZWF0ZSB0aWJibGVzICh0aGUgdGlkeXZlcnNlIHZlcnNpb24gb2YgZGF0YWZyYW1lcykNCltkcGx5cl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gZmlsdGVyLCBzdWJzZXQsIGpvaW4sIGFkZCByb3dzIHRvLCBhbmQgbW9kaWZ5IHRoZSBkYXRhICANCltzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIG1hbmlwdWxhdGUgIGNoYXJhY3RlciBzdHJpbmdzIHdpdGhpbiB0aGUgZGF0YSAoY29sbGFwc2luZyBzdHJpbmdzIHRvZ2V0aGVyLCByZXBsYWNlIHZhbHVlcywgYW5kIGRldGVjdCB2YWx1ZXMpDQpbbWFncml0dHJdKGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHBpcGUgc2VxdWVudGlhbCBjb21tYW5kcyANClt0aWR5cl0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY2hhbmdlIHRoZSBzaGFwZSBvciBmb3JtYXQgb2YgdGliYmxlcyB0byB3aWRlIGFuZCBsb25nLCB0byBkcm9wIHJvd3Mgd2l0aCBgTkFgIHZhbHVlcywgYW5kIHRvIHNlZSB0aGUgbGFzdCBmZXcgY29sdW1ucyBvZiBhIHRpYmJsZQ0KW2dnbWFwXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dtYXAvZ2dtYXAucGRmKSB8IHRvIGdlb2NvZGUgdGhlIGRhdGEgKHdoaWNoIG1lYW5zIGdldCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMpDQpbc2ZdKGh0dHBzOi8vci1zcGF0aWFsLmdpdGh1Yi5pby9zZi8pIHwgdG8gbW9kaWZ5IHRoZSBnZW9jb2RlZCBkYXRhIHNvIHRoYXQgb3ZlcmxhcHBpbmcgcG9pbnRzIGRpZCBub3Qgb3ZlcmxhcA0KW2x1YnJpZGF0ZV0oaHR0cHM6Ly9sdWJyaWRhdGUudGlkeXZlcnNlLm9yZy8pIHwgdG8gd29yayB3aXRoIHRoZSBkYXRhLXRpbWUgZGF0YSAgICANCltEVF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9EVC8pIHwgdG8gY3JlYXRlIHRoZSBpbnRlcmFjdGl2ZSB0YWJsZSAgDQpbaHRtbHRvb2xzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvaHRtbHRvb2xzL3ZlcnNpb25zLzAuNS4wKSB8IHRvIGFkZCBhIGNhcHRpb24gdG8gb3VyIGludGVyYWN0aXZlIHRhYmxlIA0KW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY3JlYXRlIHBsb3RzICANCltnZ2ZvcmNlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dmb3JjZS9nZ2ZvcmNlLnBkZikgICB8IHRvIGNyZWF0ZSBhIHBsb3Qgem9vbQ0KW2ZvcmNhdHNdKGh0dHBzOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gcmVvcmRlciBmYWN0b3IgZm9yIHBsb3QNClt3YWZmbGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9ocmJybXN0ci93YWZmbGUpIHwgdG8gbWFrZSB3YWZmbGUgcHJvcG9ydGlvbiBwbG90cyAgDQpbcG9saXNjaWRhdGFdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9wb2xpc2NpZGF0YS9wb2xpc2NpZGF0YS5wZGYpIHwgdG8gZ2V0IHBvcHVsYXRpb24gdmFsdWVzIGZvciB0aGUgc3RhdGVzDQpbZmxleGRhc2hib2FyZF0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZmxleGRhc2hib2FyZC8pICAgICB8IHRvIGNyZWF0ZSB0aGUgZGFzaGJvYXJkICANCltzaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFsbG93IG91ciBkYXNoYm9hcmQgdG8gYmUgaW50ZXJhY3RpdmUgICANCltsZWFmbGV0XShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvc2hpbnkuaHRtbCkgfCB0byBpbXBsZW1lbnQgdGhlIFtsZWFmbGV0XShodHRwOi8vbGVhZmxldGpzLmNvbS8pIChhIEphdmFTY3JpcHQgbGlicmFyeSBmb3IgbWFwcykgdG8gY3JlYXRlIHRoZSBtYXAgZm9yIG91ciBkYXNoYm9hcmQgICANClttYXBzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbWFwcy9tYXBzLnBkZikgfCB0byBjcmVhdGUgdGhlIHNpbXBsZSBsZWFmbGV0IG1hcCBleGFtcGxlICAgDQpbdmVtYmVkcl0oaHR0cHM6Ly9naXRodWIuY29tL2lqbHl0dGxlL3ZlbWJlZHIpIHwgdG8gaW5jbHVkZSBhIHZpZGVvIGluIG91ciBjYXNlIHN0dWR5ICAgDQoNCg0KIyMjIyB7LmVtcGhhc2lzX2Jsb2NrfQ0KDQoqKldhcm5pbmcgU2lnbnMqKg0KDQpGcm9tIFtTYW5keSBIb29rIFByb21pc2VdKGh0dHBzOi8vd3d3LnNhbmR5aG9va3Byb21pc2Uub3JnL2d1bi12aW9sZW5jZS9rbm93LXRoZS1zaWducy1vZi1ndW4tdmlvbGVuY2UvKS4uLg0KDQpIZXJlIGlzIGEgbGlzdCBvZiBwb3RlbnRpYWwgd2FybmluZyBzaWducyB0aGF0IGNhbiBzaWduYWwgYW4gaW5kaXZpZHVhbCBtYXkgYmUgaW4gY3Jpc2lzIGFuZC9vciBuZWVkIGhlbHA6DQoNCisgU3VkZGVubHkgd2l0aGRyYXdpbmcgZnJvbSBwZW9wbGUgYW5kIGFjdGl2aXRpZXMNCisgQ29uc2lzdGVudCBidWxseWluZyBvciBpbnRpbWlkYXRpbmcgb3RoZXJzLCBvciBiZWluZyBidWxsaWVkIGJ5IG90aGVycw0KKyBFeHRyZW1lIG1vb2Qgb3IgcGVyc29uYWxpdHkgY2hhbmdlcw0KKyBWaWN0aW0gb2YgY29uc3RhbnQgc29jaWFsIHJlamVjdGlvbg0KKyBUYWxraW5nIGFib3V0IHBsYW5zIG9yIGFjdGl2ZWx5IG1ha2luZyBwbGFucyB0byBoYXJtIHRoZW1zZWx2ZXMgb3Igb3RoZXJzDQorIEJyaW5naW5nIGEgd2VhcG9uIHRvIHNjaG9vbCDigJMgb3IgdGhyZWF0ZW5pbmcgb3IgdGFsa2luZyBhYm91dCBkb2luZyBzbw0KKyBCcmFnZ2luZyBhYm91dCBvciB3YXJuaW5nIG90aGVycyBhYm91dCBhbiB1cGNvbWluZyBhY3Qgb2YgdmlvbGVuY2UNCisgUmVjcnVpdGluZyBvdGhlcnMgdG8gam9pbiBpbiBhIHBsYW5uZWQgYWN0IG9mIHZpb2xlbmNlDQorIFdhcm5pbmcgc3R1ZGVudHMgdG8gc3RheSBhd2F5IGZyb20gc2Nob29sIG9yIGV2ZW50cw0KKyBFeHByZXNzaW5nIGZhc2NpbmF0aW9uIHdpdGggZ3VucyBhbmQvb3Igc2Nob29sIHNob290aW5ncw0KKyBFeHByZXNzaW5nIGhvcGVsZXNzbmVzcyBhYm91dCB0aGUgZnV0dXJlDQorIEV4dHJlbWUsIHByb2xvbmdlZCBzYWRuZXNzIG9yIGRpc3RyZXNzDQorIEV4cHJlc3Npbmcgb3Igc2hvd2luZyBmZWVsaW5ncyBvZiBpc29sYXRpb24NCisgQnJhZ2dpbmcgYWJvdXQgYWNjZXNzIHRvIGd1bnMNCg0KKipOT1RFKioNCg0KVGhpcyBsaXN0IGlzIG5vdCBhIGNvbXByZWhlbnNpdmUgbGlzdCBvZiB3YXJuaW5nIHNpZ25zIG5vciBkb2VzIGV4aGliaXRpbmcgb25lIG9mIHRoZXNlIHNpZ25zIGluZGljYXRlIGltbWluZW50IHZpb2xlbmNlLg0KDQpXaGVuIGNvbmNlcm5lZCBhYm91dCBzZWVpbmcgdHJvdWJsaW5nIGJlaGF2aW9ycywgdGVsbCBhIHRydXN0ZWQgYWR1bHQgb3IgY2FsbCA5MTEsIGlmIHRoZXJlIGlzIGFuIGltbWVkaWF0ZSB0aHJlYXQuDQoNCioqUmVzcG9uZCB0byBXYXJuaW5nIFNpZ25zKioNCg0KQ2FsbCA5MTEgaWYgeW91IGZlZWwgdGhlcmUgaXMgYW4gaW1tZWRpYXRlIHRocmVhdC4gDQoNCkNhbGwgWysxLTg0NC01LVNBWU5PV10odGVsOjE4NDQ1NzI5NjY5KSBpZiB5b3Ugd291bGQgdG8gc3VibWl0IGFuIGFub255bW91cyBzYWZldHkgY29uY2Vybi4NCg0KDQpJZiB5b3Ugb3IgeW91ciBjaGlsZCBvciBzdHVkZW50IGV4cGVyaWVuY2VkIGEgc2hvb3RpbmcgcGxlYXNlIHNlZSB0aGlzIFt3ZWJzaXRlXShodHRwczovL2tpZHNoZWFsdGgub3JnL2VuL3BhcmVudHMvcHRzZC5odG1sKSBhbmQgdGhpcyBbd2Vic2l0ZV0oaHR0cHM6Ly93d3cudmVyeXdlbGxtaW5kLmNvbS9zaG9vdGluZy1wdHNkLWZyb20tYS1zaG9vdGluZy0yNzk3MjAwKSBmb3IgZ3VpZGFuY2UgYWJvdXQgZGVhbGluZyB3aXRoIHRoZSB0cmF1bWEuDQoNCg0KIyMjIw0KDQojIyAqKlNlc3Npb24gSW5mbyoqDQoqKioNCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGANCg0KKipFc3RpbWF0ZSBvZiBSTWFya2Rvd24gQ29tcGlsYXRpb24gVGltZTogKioNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpybWFya2Rvd246OjpwZXJmX3RpbWVyX3N0b3AoInJlbmRlciIpDQpwdHMgPSBybWFya2Rvd246OjpwZXJmX3RpbWVyX3N1bW1hcnkoKQ0KY2F0KCJBYm91dCIsIHJvdW5kKHB0cyR0aW1lWzFdLzEwMDAgKyA1KSwgIi0iLCByb3VuZChwdHMkdGltZVsxXS8xMDAwICsgMTUpLCJzZWNvbmRzIikNCmBgYA0KDQpUaGlzIGNvbXBpbGF0aW9uIHRpbWUgd2FzIG1lYXN1cmVkIG9uIGEgUEMgbWFjaGluZSBvcGVyYXRpbmcgb24gV2luZG93cyAxMC4gVGhpcyByYW5nZSBzaG91bGQgb25seSBiZSB1c2VkIGFzIGFuIGVzdGltYXRlIGFzIGNvbXBpbGF0aW9uIHRpbWUgd2lsbCB2YXJ5IHdpdGggZGlmZmVyZW50IG1hY2hpbmVzIGFuZCBvcGVyYXRpbmcgc3lzdGVtcy4NCg0KIyMgKipBY2tub3dsZWRnbWVudHMqKg0KKioqDQoNCldlIHdvdWxkIGxpa2UgdG8gYWNrbm93bGVkZ2UgW0VsaXphYmV0aCBTdHVhcnRdKGh0dHBzOi8vd3d3Lmpoc3BoLmVkdS9mYWN1bHR5L2RpcmVjdG9yeS9wcm9maWxlLzE3OTIvZWxpemFiZXRoLWEtc3R1YXJ0KSBmb3IgYXNzaXN0aW5nIGluIGZyYW1pbmcgdGhlIG1ham9yIGRpcmVjdGlvbiBvZiB0aGUgY2FzZSBzdHVkeS4NCg0KV2Ugd291bGQgbGlrZSB0byBhY2tub3dsZWRnZSBbTWljaGFlbCBCcmVzaG9ja10oaHR0cHM6Ly9tYnJlc2hvY2suZ2l0aHViLmlvLykgZm9yIGhpcyBjb250cmlidXRpb25zIHRvIHRoaXMgY2FzZSBzdHVkeSBhbmQgZGV2ZWxvcGluZyB0aGUgYE9DU2RhdGFgIHBhY2thZ2UuIA0KDQpXZSB3b3VsZCBhbHNvIGxpa2UgdG8gYWNrbm93bGVkZ2UgdGhlIFtCbG9vbWJlcmcgQW1lcmljYW4gSGVhbHRoIEluaXRpYXRpdmVdKGh0dHBzOi8vYW1lcmljYW5oZWFsdGguamh1LmVkdS8pIGZvciBmdW5kaW5nIHRoaXMgd29yay4gDQoNCg0KDQo=