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 Jager, Leah and Taub, Margaret and Hicks, Stephanie C. (2020). https://github.com/opencasestudies/ocs-youth-disconnection-case-study. Disparities in Youth Disconnection.

To access the GitHub repository for this case study see here: https://github.com//opencasestudies/ocs-bp-youth-disconnection.

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.


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

Reading Time Method
84 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 8 13
FORCAST 10 15
SMOG 11 16

Please help us by filling out our survey.

Motivation


First, let’s discuss the meaning of the term “youth disconnection”.

According to Measure of America (a nonpartisan project of the nonprofit Social Science Research Council that is focused on opportunity in the United States), disconnected youth are:

“young people between the ages of 16 and 24 who are neither working nor in school

The group states that such disconnection hinders these individuals to acquire skills and create relationships necessary to have a successful adulthood.

The group goes on to state that:

“people who experience a period of disconnection as young adults go on to earn less and are less likely to be employed, own a home, or report good health by the time they reach their thirties”

Disconnected youth are also referred to as opportunity youth, which has the added positive connotation that promoting such individuals can be beneficial not only for these individuals, but also for their communities and for society.

Good news: According to this report, the youth disconnection is generally showing decreasing trends for the past 7 years.

Bad news: The same report shows racial and ethnic disparities, where some groups are showing increased rates of disconnection.

In this case study, we will expand beyond the Measure of America annual report to take a deeper look at differences in disconnection between different subgroups of youths. Identifying youths particularly at risk or disconnected, can help inform the design of targeted prevention and re-engagement strategies. To do this, we use the following article as our motivation for this case study.

Mendelson, T., Mmari, K., Blum, R. W., Catalano, R. F. & Brindis, C. D. Opportunity Youth: Insights and Opportunities for a Public Health Approach to Reengage Disconnected Teenagers and Young Adults. Public Health Rep 133, 54S-64S (2018).

The article describes strategies for prevention of disconnection and re-engagement of disconnected youth and how such interventions could greatly positively impact opportunity youth for the entire trajectory of their lives and for future generations. It also points out that indeed there are disparities among different racial/ethnic groups.

Main Questions


Our main questions:

  1. How have youth disconnection rates in American youth changed since 2008?
  2. In particular, how has this changed for different gender and ethnic groups? Are any groups particularly disconnected?

Learning Objectives


In this case study, we will demonstrate how to import and wrangle data available in a Portable Document Format (PDF). We will especially focus on using packages and functions from the tidyverse, such as dplyr, ggplot2. 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 legible 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 PDF files using images and the magick package
  2. Apply action verbs in dplyr for data wrangling
  3. How to reshape data by pivoting between “long” and “wide” formats and separating columns into additional columns (tidyr)
  4. How to fill in data based on previous values (tidyr)
  5. How to create data visualizations with ggplot2 that are in a similar style to an existing image
  6. How to add images to plots using cowplot
  7. How to create effective bar plots to for multiple comparisons, including adding gaps between bars in bar plots, adding figure legends to the plot area, and adding comparison lines (ggplot2)

Statistical Learning Objectives:

  1. Implementation of the Mann-Kendall trend test
  2. Interpretation of the Mann-Kendall trend test
  3. Difference between linear regression and Mann-Kendall trend test


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

library(here)
library(pdftools)
library(tesseract)
library(magick)
library(knitr)
library(readr)
library(dplyr)
library(stringr)
library(magrittr)
library(tidyr)
library(tibble)
library(ggplot2)
library(directlabels)
library(cowplot)
library(forcats)
library(Kendall)
library(patchwork)
library(DT)
library(OCSdata)

Packages used in this case study:

Package Use in this case study
here to easily load and save data
pdftools to import PDF documents
magick for importing images and extracting text from images
tesseract for extracting text from images with magick
knitr for showing images in reports
readr for saving files
dplyr to filter, subset, join, add rows to, and modify the data
stringr to manipulate strings
magrittr to pipe sequential commands
tidyr to change the shape or format of tibbles to wide and long, to drop rows with NA values, to separate a column into additional columns, and to fill out values based on previous values
tibble to create tibbles
ggplot2 to create plots
directlabels to add labels directly to lines in plots
cowplot to add images to plots
forcats to reorder factor for plot
kendall to implement the Mann-Kendall trend test in R
patchwork to combine plots
DT Interactive tables
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


So how does youth disconnection happen and what impact does it have?

There are many known risk factors, which have been identified in a variety of contexts (from family, friends, school, community, society) including:

  • poverty (disconnected youth are nearly twice as likely to live in poverty and receive Medicaid)
  • racial/ethnic disparities (findings suggest that these persist even when controlling for income)
  • residential environment (in 2016 while 11.7% was the national average, 24% of people age 16-24 in the rural South were disconnected)
  • poor academic performance
  • poor mental health
  • substance use disorders
  • parental unemployment
  • trauma exposure
  • association with socially deviant peers
  • school policies such as “one strike and you’re out” - which is a zero tolerance school expulsion policy and shown to increase dropouts and incarceration rates

These risk factors make it more likely for young people to miss out on education, training, and networking that can act as a foundation for a successful career.

There are also many known negative consequences associated with youth disconnection including but not limited to:

  • chronic unemployment
  • poverty
  • poor mental health and poor general health (in a 2002 study - youths disconnected for 6 or more months were 3 times more likely to develop depression or other mental health disorder)
  • criminal behavior (in a 2002 study - youths disconnected for 6 or more months were 5 times more likely to have a criminal record)
  • incarceration
  • early mortality

Photo by Jon Tyson on Unsplash

Furthermore, in 2012 it was estimated that each disconnected youth costs taxpayers $250,000 during a lifetime due to lost tax revenue and costs for social services, heath care and criminal justice.

Youth disconnection can be described as a continuum, as some youths will be disconnected for a brief time, while others are chronically disconnected. Additionally, while an individual who is out of school and work and also has poor support from the relationships of others may be further disconnected than an individual who has social support.

Here is an illustration of risk factors, protective factors and the continuum of disconnection:

Strategies to mitigate youth disconnection


Many programs have identified useful strategies in re-engaging disconnected youth or preventing disconnection of youth.

Generally speaking, most programs focus on re-engagement strategies, however, prevention strategies are likely to be just as important.

Research suggests that active involvement with at risk youth from infancy and across multiple developmental stages through young adulthood would be the most beneficial.

In fact, the quality of parental care-giving of infants age 6-24 months has actually been shown to be a predictor of high school dropout rates! Thus early interventions may be very important and consistent continual engagement may prevent further disconnection of youths.

Prevention strategies include:

Want to learn more about how to prevent and mitigate youth disconnection?
Or do you know youths who are disconnected?

See the program directory at youth.gov and this program listing focused on Maryland but including other locations for listings of programs dedicated to re-engaging disconnected youth or preventing disconnection.

Also, see The Center for Communities That Care and the PROSPER program for particular examples.

The statistics used in this section came from this article.

Limitations


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

  1. This data used in the Measure of America project is derived from American Community Survey (ACS), which excludes or under-represents certain opportunity youth groups, such as youths in the juvenile justice system, youths in the foster care system, and homeless youths as the survey is conducted on households. Furthermore, youths who may be more disconnected for other reasons besides not being in work or school, such as dealing with the added challenge of being a teenage mother, or being abused are not available in this dataset. Thus, this data likely underestimates youth disconnection rates. Furthermore, the data used in these reports are collected from human participants; this presents the potential for response bias, as there is the potential that participants in the sampling frame may for a variety of reasons report inaccurate information.

  2. Data about certain group intersections (meaning for example individuals of a particular gender and ethnicity) or particular groups in general such as specific ethnicities or gender or sexual identity groups such as LGBTQIA+ (lesbian/gay/bisexual/pansexual/transgender/genderqueer/queer,questioning/intersexed/agender/asexual) is unfortunately not available in the data used in this analysis and in most research about this topic. Luckily however, recent years of the ACS survey has more detailed information about a greater number of racial and ethnic groups and racial/ethnic intersections.

  3. The statistical procedures we are using may be overly simplistic. In all data analysis, we need to be wary about deriving meaning from the statistical procedures we use. Furthermore, the data we are using is summary level showing averages for large groups. Thus, it is difficult to know how reliable the summary estimates are, as we don’t have information about the variability of the data within a group.

  4. Using image processing tools can be very helpful. The manner in which data is obtained with image processing tools is what we would describe as a black box process, a process with known inputs and outputs but unknown mechanics. Because we are unaware of how our outputs are generated from our inputs, we need to be wary of the output. With the small output we are creating in this case study, a visual inspection should suffice.

What are the data?


In this case study we will be using data related to youth disconnection from the two following reports from the Measure of America project:

Measure of America is a nonpartisan project of the nonprofit Social Science Research Council founded in 2007 to create easy-to-use yet methodologically sound tools for understanding well-being and opportunity in America. Through reports, interactive apps, and custom-built dashboards, Measure of America works with partners to breathe life into numbers, using data to identify areas of highest need, pinpoint levers for change, and track progress over time.

  1. Lewis, Kristen. Making the Connection: Transportation and Youth Disconnection . New York: Measure of America, Social Science Research Council, 2019. (Data up to 2017)

  1. Lewis, Kristen. A Decade Undone: Youth Disconnection in the Age of Coronavirus. New York: Measure of America, Social Science Research Council, 2020. (Data up to 2018)

The data used in these reports comes from the American Community Survey (ACS), which is the largest survey conducted by the United States Census Bureau. The survey started in 2005 and collects data for 3.5 million households annually. Data is collected about ancestry, citizenship, income, employment, disability among many other aspects. See here for more detailed information about the survey.

According to Wikipedia:

Data is collected by internet, mail, telephone interviews and in-person interviews… About 95 percent of households across all response modes ultimately respond… ACS responses are confidential"

It is a mandatory survey, it is governed by federal laws that could impose a fine of as much as $5,000 for refusing to participate.

We are particularly interested in the following tables on the last page of the Measure of America 2019 report:

Also, we are particularly interested in the tables on the following pages from the Measure of America 2020 report:

Data Import


If you did not clone the GitHub repository, you may download the raw data files, including the pdf file of the report and the screenshots, using the OCSdata package:

# install.packages("OCSdata")
# library(OCSdata)

raw_data("ocs-bp-youth-disconnection", outpath = getwd())
# This function creates a "OCSdata" sub-directory in your current working directory.
# It also creates a "raw" sub-directory within "data" which contains the .pdf file.

Importing with pdftools


If you still have trouble obtaining the pdf file, you can download it using this link.

One way to import data from a pdf is to use the pdf_text() function of the pdftools package. In our case, we downloaded the Making_the_Connection.pdf file 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 of the here package to specify the path or location of the document that we want to import. 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 Making_the_Connection.pdf file within the raw directory within the data directory within a directory where our .Rproj file is located. Starting from “data”, we separate the names of these directories using commas.


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 and here to learn more about the here package.


Note the next chunk of code will only work for you if you set up your file structure in the same way as stated above.

pdf_tools_example <- 
  pdftools::pdf_text(here::here("data", "raw", "Making_the_Connection.pdf"))

We can take a look at the output for the page with our table of interests by simply using brackets [] around the page number. The page we are interested in (although labeled 39 in the report) is the 44th page, which looks like this:

# Scroll through the output!
pdf_tools_example[44]
[1] "Youth Disconnection by Gender and by Race and Ethnicity\n\n\n\n\n                                                                                                                                               I NDI CATOR TA BLE S\n                                DISCONNECTED YOUTH\nMAJOR RACIAL AND                 RATE (% ages 16–24)                                  2017          CHANGE IN RATE\nETHNIC GROUPS            2008   2010       2012           2014     2016        (%)           (#)     2010–2017 (%)\nUnited States            12.6   14.7       14.1           13.2     11.7        11.5     4,501,800       -22.1\nMale                     12.3   15.2       14.5           13.3     12.1        11.8     2,382,500       -22.5\nFemale                   12.9   14.1       13.7           13.0     11.2        11.1     2,119,400       -21.7\nASIAN                    7.1     8.5       7.8            7.9       6.6        6.6      145,600         -21.7\nAsian Male               6.3     8.3          7.4         7.2       6.7        6.5       73,000         -21.4\nAsian Female             7.9     8.6          8.1         8.6       6.6        6.7       72,600         -22.0\nWHITE                    9.7    11.7       11.2           10.8      9.7        9.4      1,961,700       -20.1\nWhite Male               9.5    12.3       11.5           10.8    10.0         9.6      1,031,200       -22.4\nWhite Female             10.0   11.1       10.8           10.7      9.4        9.1       930,600        -17.4\nLATINO                   16.7   18.5       17.3           15.2     13.7        13.2     1,157,300       -28.7\nLatino Male              13.6   16.8       16.0           14.0     12.6        12.4      562,600        -26.0\nLatina Female            20.2   20.3       18.8           16.5     14.8        13.9      594,700        -31.5\nBLACK                    20.4   22.5       22.4           20.6     17.2        17.9     999,700         -20.6\nBlack Male               23.7   26.0       25.6           23.5     20.1        20.8      591,600        -19.8\nBlack Female             17.0   19.0       19.3           17.6     14.2        14.8      408,000        -22.1\nNATIVE AMERICAN          24.4   28.8       27.0           26.3     25.8        23.9      67,700         -17.1\nNative American Male     25.0   30.9       28.0           26.9     28.1        23.3      33,200         -24.5\nNative American Female   23.9   26.7       25.9           25.6     23.4        24.5      34,500         -8.4\n\n\n\n\n                                       2017                                                                            2017\n ASIAN SUBGROUPS                (%)                 (#)          LATINO SUBGROUPS                               (%)             (#)\n\n United States                  11.5       4,501,800\n Male                           11.8          2,382,500          LATINO                                         13.2          1,157,300\n Female                         11.1          2,119,400          Latino Male                                    12.4           562,600\n ASIAN                          6.6           145,600            Latina Female                                  13.9           594,700\n Asian Male                     6.5            73,000            SOUTH AMERICAN                                 8.4            37,600\n Asian Female                   6.7            72,600            South American Male                            9.1            20,400\n CHINESE                        4.3            23,800            South American Female                          7.7            17,200\n Chinese Male                   4.7            12,700            CENTRAL AMERICAN                               12.0           93,100\n Chinese Female                 3.9            11,100            Central American Male                          9.3            37,900\n VIETNAMESE                     5.5            13,500            Central American Female                        15.0           55,200\n Vietnamese Male                7.5             9,300\n                                                                 MEXICAN                                        13.3          762,400\n Vietnamese Female              3.4             4,200\n                                                                 Mexican Male                                   12.2           358,200\nINDIAN                          5.9            22,300\n                                                                 Mexican Female                                 14.4           404,200\nIndian Male                     4.1             8,000            OTHER LATINO                                   13.6           44,800\nIndian Female                   7.8            14,300\n                                                                 Other Latino Male                              15.3           27,600\n PAKISTANI                      6.4             4,900\n                                                                 Other Latina Female                            11.5           17,300\n Pakistani Male\n                                                                 PUERTO RICAN, DOMINICAN, CUBAN                 15.1          211,200\n Pakistani Female\n                                                                 PR, DR, Cuban Female                           15.7           114,500\n KOREAN                         6.5            11,200\n                                                                 PR, DR, Cuban Female                           14.4           96,600\n Korean Male                    8.0             6,900\n Korean Female                  5.0             4,200\n                                                                 NOTE: Blank cells indicate the estimate is unreliable\n TWO OR MORE                    6.6             4,000\n Two or More Male\n Two or More Female\n\nFILIPINO                        7.3            23,400\nFilipino Male                   6.5            10,800\nFilipino Female                 8.1            12,700\nHMONG                           14.0            8,300\nHmong Male                      18.6            5,700\nHmong Female\n\n\n\n\nMAKING THE CONNECTION | Transportation and Youth Disconnection                                                                            39\n"

From the output, it’s clear that a relatively large amount of manipulation will be required to wrangle this data. If you are interested in learning more about the pdftools package, please see this case study and this case study.

Question Opportunity

If you are familiar with pdftools, spend 3-5 minutes trying to extract the multiple tables from page 44.

While you might find the question above to not be impossible using the pdftools package, you might find it a bit challenging because of how the multiple tables are displayed on this page.

While our output may be reproducible, this process may be too time consuming.

Let’s consider alternative approaches to importing these data.

Next, we will demonstrate how to produce reproducible tables with image processing software in R using a package called magick which allows for the extraction of text from images. The advantage of this option, is that we can take a screenshot of just a piece of the page to wrangle.

Importing with magick


We will now import the data using the magick package which allows for the importation of data from images.

First, we will take a screenshot (specifically create a .png file) of the top part of the gender, race, and ethnicity table on the last page of the 2019 Measure of America Report. We are only interested in the percentage of disconnection across the years, so we don’t need our screenshot to include the last couple of columns.

Here, we show what this file looks like in this rendered rmarkdown website by using the include_graphics() function of the knitr package.

knitr::include_graphics(here::here("data", "raw", "Major_ethnic_groups_screenshot.png"))

You can download this image file from our GitHub repostory to follow along here.

Next, we use the image_read() function of the magick package to import this image.

Then, we use the image_info() function to make sure that the import worked and to get information about the size, format and color of the image.

Major_racial_ethnic_groups <- 
  magick::image_read(here::here("data", "raw", "Major_ethnic_groups_screenshot.png"))

magick::image_info(Major_racial_ethnic_groups)
# A tibble: 1 x 7
  format width height colorspace matte filesize density
  <chr>  <int>  <int> <chr>      <lgl>    <int> <chr>  
1 PNG     1086    867 sRGB       TRUE    368488 72x72  

We get some basic information, such as the file format is a PNG, the width and height of the image, etc. Let’s take a closer look at our image in R! Now that we have imported it to see this image, we simply need to type the name of the image.

Major_racial_ethnic_groups

Nice!

Let’s import a couple more images just for fun. Here we will import an image directly from a URL.

ggplot2_url <- "https://d33wubrfki0l68.cloudfront.net/2c6239d311be6d037c251c71c3902792f8c4ddd2/12f67/css/images/hex/ggplot2.png"

ggplot2_logo <- image_read(ggplot2_url)
ggplot2_logo

Now we will use the image_ocr() function of the magick package to extract the text from the ggplot2 logo image. This function uses the tesseract package which has tools for optical character recognition (OCR), hence the ocr in the function name. This allows the function to identify text in images. These OCR tools have often been developed using machine learning in which an algorithm was trained on images with and without text to “learn” to recognize text. See here to learn more about how OCR works.

magick::image_ocr(ggplot2_logo)
[1] "ggplot2\n"

Awesome! We were able to extract text from this hex sticker!

One thing to keep in mind is that this doesn’t always work. Unusual font, angles text, or particular colors can be difficult for the OCR to recognize.

Here is an example that does not work with the current version of magick:

tidyverse_url <- "https://tidyverse.tidyverse.org/logo.png"

tidyverse_logo <- image_read(tidyverse_url)
tidyverse_logo

magick::image_ocr(tidyverse_logo)
[1] ""

This is likely do to the background on this particular hex sticker. So sometimes this process requires a bit of trial and error.

OK! So now let’s try extracting the text for our Major_racial_ethnic_groups image.

Magor Racial and Ethnic Groups

major_groups <- magick::image_ocr(Major_racial_ethnic_groups)
major_groups
[1] "United States 12.6 14.7 14.1 13.2 11.7 11.5\nMale 12.3 15.2 14.5 13.3 12.1 11.8\nFemale 12.9 14.1 13.7 13.0 11.2 11.1\nASIAN 7.1 8.5 78 79 6.6 6.6\nAsian Male 6.3 8.3 74 7.2 6.7 6.5\nAsian Female 7.9 8.6 8.1 8.6 6.6 6.7\nWHITE 9.7 11.7 11.2 10.8 9.7 9.4\nWhite Male 9.5 12.3 11.5 10.8 10.0 9.6\nWhite Female 10.0 11.1 10.8 10.7 9.4 9.1\nLATINO 16.7 18.5 17.3 15.2 13.7 13.2\nLatino Male 13.6 16.8 16.0 14.0 12.6 12.4\nLatina Female 20.2 20.3 18.8 16.5 14.8 13.9\nBLACK 20.4 22.5 22.4 20.6 17.2 17.9\nBlack Male 23.7 26.0 25.6 23.5 20.1 20.8\nBlack Female 17.0 19.0 19.3 17.6 14.2 14.8\nNATIVE AMERICAN 24.4 28.8 27.0 26.3 25.8 23.9\nNative American Male 25.0 30.9 28.0 26.9 28.1 23.3\nNative American Female 23.9 26.7 25.9 25.6 23.4 24.5\n"

This looks like it worked fairly well!

Excellent, now we need to do the same for the other two tables on the bottom half of this page that we are interested in:

So now we will take some screenshots of the other tables of interest. We will later explain why we took the specific screenshots that we did, but for now just focus on importing the data. In general,it is helpful to leave off any additional header information that you don’t intend to include in the actual table. In addition empty spaces in tables can cause the need for additional images.

Asian Subgroups 2017


First, we are also interested in data about the Asian subgroups in 2017.

If you have not done so already, you can download the next image file here.

Question Opportunity

Can you recall the command to import an image into R using the magick package?


Click here to reveal the code.
asian_sub_2017 <- image_read(here::here("data", "raw", "asian_subgroups_2017.png"))

asian_sub_2017

Question Opportunity

Can you recall the command to extract the text from an image using the magick package?


Click here to reveal the code.
asian_sub_2017 <- image_ocr(asian_sub_2017)

asian_sub_2017
[1] "United States 11.5\nMale 11.8\nFemale 11.1\nASIAN 6.8\nAsian Male 65\nAsian Female 67\nCHINESE 43\nChinese Male AT\nChinese Female 3.9\nVIETNAMESE 55\nVietnamese Male 75\nVietnamese Female 3.4\nINDIAN 5.9\nIndian Male 4.1\nIndian Female 78\nPAKISTANI 64\nPakistani Male\n\nPakistani Female\n\nKOREAN 65\nKorean Male 8.0\nKorean Female 5.0\nTWO OR MORE 68\nTwo or More Male\n\nTwo or More Female\nFILIPINO. S\231~\231~S~—~SS\nFilipino Male 65\nFilipino Female 81\nHMONG 14.0\nHmong Male 18.6\n"

Trial and error during the wrangling section will demonstrate that we needed some additional images. We will import these now.

If you have not done so already, you can download the next image files to follow along here and here and here.

asian_sub_2017_A <- image_read(here("data", "raw", "asian_sub_2017_A.png"))
asian_sub_2017_B <- image_read(here("data", "raw", "asian_sub_2017_B.png"))
asian_sub_2017_C <- image_read(here("data", "raw", "asian_sub_2017_C.png"))

asian_sub_2017_A

asian_sub_2017_B

asian_sub_2017_C

asian_sub_2017_A <- image_ocr(asian_sub_2017_A)
asian_sub_2017_B <- image_ocr(asian_sub_2017_B)
asian_sub_2017_C <- image_ocr(asian_sub_2017_C)

asian_sub_2017_A
[1] "United States 11.5\nMale 11.8\nFemale 11.1\nASIAN 6.6\nAsian Male 6.5\nAsian Female 6.7\nCHINESE 4.3\nChinese Male 4.7\nChinese Female 3.9\nVIETNAMESE 5.5\nVietnamese Male 75\nVietnamese Female 3.4\nINDIAN 5.9\nIndian Male 4.1\nIndian Female 7.8\nPAKISTANI 6.4\n"
asian_sub_2017_B 
[1] "KOREAN 6.5\nKorean Male 8.0\nKorean Female 5.0\nTWO OR MORE 6.6\n"
asian_sub_2017_C
[1] "FILIPINO 7.3\nFilipino Male 6.5\nFilipino Female 8.1\nHMONG 14.0\nHmong Male 18.6\n"

OK, now moving on to the last table on this page

Latinx Subgroups 2017

Again after trial and error, three screenshots were determined best for importing this data.

The names of the files for the screenshots are latinx_sub_2017_A.png, "latinx_sub_2017_B.png, and "latinx_sub_2017_C.png". They are located in the “raw” subdirectory of the “data” directory in our GitHub repository.

If you have not done so already, you can download the next image files to follow along here and here and here.

Question Opportunity

Can you recall the commands to import and extract the data?


Click here to reveal the code.
latinx_imageA <- image_read(here::here("data", "raw", "latinx_sub_2017_A.png"))
latinx_imageB <- image_read(here::here("data", "raw", "latinx_sub_2017_B.png"))
latinx_imageC <- image_read(here::here("data", "raw", "latinx_sub_2017_C.png"))

latinx_sub_2017_A <- image_ocr(latinx_imageA)
latinx_sub_2017_B <- image_ocr(latinx_imageB)
latinx_sub_2017_C <- image_ocr(latinx_imageC)

latinx_imageA

latinx_sub_2017_A
[1] "LATINO 13.2\nLatino Male 12.4\nLatina Female 13.9\n"
latinx_imageB

latinx_sub_2017_B
[1] "SOUTH AMERICAN 8.4\nSouth American Male 9.1\nSouth American Female 7.7\nCENTRAL AMERICAN 12.0\nCentral American Male 9.3\nCentral American Female 15.0\nMEXICAN 13.3\nMexican Male 12.2\nMexican Female 14.4\nOTHER LATINO 13.6\nOther Latino Male 15.3\nOther Latina Female 11.5\n"
latinx_imageC

latinx_sub_2017_C
[1] "PUERTO RICAN, DOMINICAN, CUBAN 15.1\nPR, DR, Cuban Female 15.7\nPR, DR, Cuban Female 14.4\n"

Great! Now let’s import the text from some images for the 2018 data from the other report.

Asian Subgroups 2018


Recall that this was the page with the table of interest for the Asian subgroups with 2018 data:

As you can see, the data for the subgroups is shown in the table but the overall data for Asians is located in the text.

We will use a screenshot of each to extract the data for this year.

Trial and error indicated that again dividing the table into multiple screenshots improved the text extraction:

asian_sub_2018_A <- image_read(here::here("data", "raw", "asian_sub_2018_A.png"))
asian_sub_2018_A <- image_ocr(asian_sub_2018_A)
asian_sub_2018_B <- image_read(here::here("data", "raw", "asian_sub_2018_B.png"))
asian_sub_2018_B <- image_ocr(asian_sub_2018_B)

Finally let’s get the Latinx subgroup data for 2018

Latinx Subgroups 2018


Recall that this was the page with the table of interest for the Latinx subgroup 2018 data (published in 2020):

In this case only a single image was needed:

latinx_sub_2018 <- image_read(here::here("data", "raw", "latinx_subgroups_2018.png"))
latinx_sub_2018 <- image_ocr(latinx_sub_2018)
latinx_sub_2018
[1] "SOUTH :\n\nAMERICAN : 8.0\nMen 7.5\nMEXICAN : 12.9\nMen 12.0\nWomen 13.8\nPR, DR, CUBAN 13.7\nMen 14.9\nWomen 12.4\nCENTRAL :\n\nAMERICAN : 13.7\nMen 11.8\nWomen : 15.9\n"

Excellent! Now let’s save all our imported data for users to start at later sections.

save(
  major_groups,
  asian_sub_2017,
  asian_sub_2017_A, 
  asian_sub_2017_B,
  asian_sub_2017_C, 
  latinx_sub_2017_A,
  latinx_sub_2017_B,
  latinx_sub_2017_C,
  asian_sub_2018_A,
  asian_sub_2018_B,
  latinx_sub_2018,
  file = here::here("data", "imported", "imported_data.rda"))

Data Wrangling


Now let’s try extracting the text from our image files.

If you have been following along but stopped and are starting here you could load the imported data using the following command:

load(here::here("data", "imported", "imported_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 load the imported data into your R environment using the following code:

# library(OCSdata)
imported_data("ocs-bp-youth-disconnection", outpath = getwd())
load(here::here("OCSdata", "data", "imported", "imported_data.rda"))

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 within a subdirectory called “imported” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "imported", "imported_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 and here to learn more about the here package.



Major Racial and Ethnic Groups


The first image we imported looks like this.

knitr::include_graphics(here::here("data", "raw", "Major_ethnic_groups_screenshot.png"))

After extracting the text we got this:

major_groups 
[1] "United States 12.6 14.7 14.1 13.2 11.7 11.5\nMale 12.3 15.2 14.5 13.3 12.1 11.8\nFemale 12.9 14.1 13.7 13.0 11.2 11.1\nASIAN 7.1 8.5 78 79 6.6 6.6\nAsian Male 6.3 8.3 74 7.2 6.7 6.5\nAsian Female 7.9 8.6 8.1 8.6 6.6 6.7\nWHITE 9.7 11.7 11.2 10.8 9.7 9.4\nWhite Male 9.5 12.3 11.5 10.8 10.0 9.6\nWhite Female 10.0 11.1 10.8 10.7 9.4 9.1\nLATINO 16.7 18.5 17.3 15.2 13.7 13.2\nLatino Male 13.6 16.8 16.0 14.0 12.6 12.4\nLatina Female 20.2 20.3 18.8 16.5 14.8 13.9\nBLACK 20.4 22.5 22.4 20.6 17.2 17.9\nBlack Male 23.7 26.0 25.6 23.5 20.1 20.8\nBlack Female 17.0 19.0 19.3 17.6 14.2 14.8\nNATIVE AMERICAN 24.4 28.8 27.0 26.3 25.8 23.9\nNative American Male 25.0 30.9 28.0 26.9 28.1 23.3\nNative American Female 23.9 26.7 25.9 25.6 23.4 24.5\n"

We appear to have lost the column names (likely because of the different background color) but the values look pretty good.

You may notice that there are lots of \n values in the text from our image. These are newline characters, which denote the end of a line of text and the start of a new line of text.

To deal with these, we use the str_split() function of the stringr package to split based on the \n characters in the output, which turns out to be a list. Then, we can “unlist” the output using the base R unlist() function. By base R, we mean that the function it is loaded automatically in an R session. Finally, we use the as_tibble() function of the tibble package to convert the data into tibble format, which is the tidyverse version of a data frame. This will allow us to see the values in the table much better.

To do all of these sequential steps efficiently we will use a method called piping.


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.
It takes the object in front of the %>% pipe operator as the first argument of the function after the pipe operator.

For example, the following two lines of code are equivalent.

head(mtcars, n = 2)
              mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4
Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4
mtcars %>% head(n = 2)
              mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4
Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4

 major_groups <- 
  major_groups %>%
  stringr::str_split(pattern ="\n") %>%
  unlist() %>%
  tibble::as_tibble()

major_groups %>% 
  print(n = 20)
# A tibble: 19 x 1
   value                                                 
   <chr>                                                 
 1 "United States 12.6 14.7 14.1 13.2 11.7 11.5"         
 2 "Male 12.3 15.2 14.5 13.3 12.1 11.8"                  
 3 "Female 12.9 14.1 13.7 13.0 11.2 11.1"                
 4 "ASIAN 7.1 8.5 78 79 6.6 6.6"                         
 5 "Asian Male 6.3 8.3 74 7.2 6.7 6.5"                   
 6 "Asian Female 7.9 8.6 8.1 8.6 6.6 6.7"                
 7 "WHITE 9.7 11.7 11.2 10.8 9.7 9.4"                    
 8 "White Male 9.5 12.3 11.5 10.8 10.0 9.6"              
 9 "White Female 10.0 11.1 10.8 10.7 9.4 9.1"            
10 "LATINO 16.7 18.5 17.3 15.2 13.7 13.2"                
11 "Latino Male 13.6 16.8 16.0 14.0 12.6 12.4"           
12 "Latina Female 20.2 20.3 18.8 16.5 14.8 13.9"         
13 "BLACK 20.4 22.5 22.4 20.6 17.2 17.9"                 
14 "Black Male 23.7 26.0 25.6 23.5 20.1 20.8"            
15 "Black Female 17.0 19.0 19.3 17.6 14.2 14.8"          
16 "NATIVE AMERICAN 24.4 28.8 27.0 26.3 25.8 23.9"       
17 "Native American Male 25.0 30.9 28.0 26.9 28.1 23.3"  
18 "Native American Female 23.9 26.7 25.9 25.6 23.4 24.5"
19 ""                                                    

OK, this looks pretty good!

The only issue is that some values appear to be missing a decimal point.

No worries though, we can modify the entire table in a reproducible way to get those decimal places back. However, first we need to do some other wrangling steps first.

First, let’s separate the first column about ethnicities with the values in the subsequent columns. We can do so using the separate() function of the tidyr package based on regular expressions. Regular expressions (abbreviated regex) are notation shortcuts that describe patterns in character strings. See here for an RStudio cheetsheat about them.

We want to separate by instances where a letter is followed by a space and then a number.

We can specify any letter by using the regex [:alpha:] notation and any number by using the regex [:digit:] notation. We could have listed every letter that we saw the first column ending with like so s|e|E|O|K|N but this would not be as reproducible (meaning maybe this would not work as well next year if a new group were added that ended in a different letter), and we might make a mistake. This is why the regex are so useful.

We can indicate that we want a space by using this regex:

Now to specify that we want to see a letter first followed by a space, followed by a digit, we need to use a look around:

We will use the “preceded by” and “followed by” look arounds. Thus (?<=[[:alpha:]]) stands for any letter that appears before a space \\s that is followed by any digit (?=[0-9]). Altogether the pattern we want to separate by looks like this: "(?<=[[:alpha:]])\\s(?=[0-9])".

Now to separate the value column into two columns, we can use the separate() function of the tidyr package to do this. This will allow us to not only split the rows by our regex expression, but also to create column names.

There are three important arguments for the separate() function:
- col - this specifies what column you are separating
- into - this specifies the names of the new columns you are creating
- sep - this specifies what character string to look for to separate by

Thus we will separate the value column into Group and years columns.

major_groups <- 
  major_groups %>%
  tidyr::separate(col = value, 
                  into = c("Group", "Years"), 
                  sep = "(?<=[[:alpha:]])\\s(?=[0-9])")

major_groups
# A tibble: 19 x 2
   Group                    Years                        
   <chr>                    <chr>                        
 1 "United States"          12.6 14.7 14.1 13.2 11.7 11.5
 2 "Male"                   12.3 15.2 14.5 13.3 12.1 11.8
 3 "Female"                 12.9 14.1 13.7 13.0 11.2 11.1
 4 "ASIAN"                  7.1 8.5 78 79 6.6 6.6        
 5 "Asian Male"             6.3 8.3 74 7.2 6.7 6.5       
 6 "Asian Female"           7.9 8.6 8.1 8.6 6.6 6.7      
 7 "WHITE"                  9.7 11.7 11.2 10.8 9.7 9.4   
 8 "White Male"             9.5 12.3 11.5 10.8 10.0 9.6  
 9 "White Female"           10.0 11.1 10.8 10.7 9.4 9.1  
10 "LATINO"                 16.7 18.5 17.3 15.2 13.7 13.2
11 "Latino Male"            13.6 16.8 16.0 14.0 12.6 12.4
12 "Latina Female"          20.2 20.3 18.8 16.5 14.8 13.9
13 "BLACK"                  20.4 22.5 22.4 20.6 17.2 17.9
14 "Black Male"             23.7 26.0 25.6 23.5 20.1 20.8
15 "Black Female"           17.0 19.0 19.3 17.6 14.2 14.8
16 "NATIVE AMERICAN"        24.4 28.8 27.0 26.3 25.8 23.9
17 "Native American Male"   25.0 30.9 28.0 26.9 28.1 23.3
18 "Native American Female" 23.9 26.7 25.9 25.6 23.4 24.5
19 ""                       <NA>                         

Looks good!

Let’s also get rid of the all caps for the major categories of the Group column. We can convert the words to only capitalize the first letter using the str_to_title() function of the stringr package. To specifically modify the Group column we can use the mutate function of the dplyr package.

We are also going to use a special pipe operator (%<>%) from the magrittr package called the compound assignment pipe-operator or sometimes the double pipe operator.

This allows us to use the major_groups as our input and reassign it at the end after all the subsequent steps have been performed, although in this case it is only one step.

major_groups %<>%
  mutate(Group = stringr::str_to_title(Group))

major_groups
# A tibble: 19 x 2
   Group                    Years                        
   <chr>                    <chr>                        
 1 "United States"          12.6 14.7 14.1 13.2 11.7 11.5
 2 "Male"                   12.3 15.2 14.5 13.3 12.1 11.8
 3 "Female"                 12.9 14.1 13.7 13.0 11.2 11.1
 4 "Asian"                  7.1 8.5 78 79 6.6 6.6        
 5 "Asian Male"             6.3 8.3 74 7.2 6.7 6.5       
 6 "Asian Female"           7.9 8.6 8.1 8.6 6.6 6.7      
 7 "White"                  9.7 11.7 11.2 10.8 9.7 9.4   
 8 "White Male"             9.5 12.3 11.5 10.8 10.0 9.6  
 9 "White Female"           10.0 11.1 10.8 10.7 9.4 9.1  
10 "Latino"                 16.7 18.5 17.3 15.2 13.7 13.2
11 "Latino Male"            13.6 16.8 16.0 14.0 12.6 12.4
12 "Latina Female"          20.2 20.3 18.8 16.5 14.8 13.9
13 "Black"                  20.4 22.5 22.4 20.6 17.2 17.9
14 "Black Male"             23.7 26.0 25.6 23.5 20.1 20.8
15 "Black Female"           17.0 19.0 19.3 17.6 14.2 14.8
16 "Native American"        24.4 28.8 27.0 26.3 25.8 23.9
17 "Native American Male"   25.0 30.9 28.0 26.9 28.1 23.3
18 "Native American Female" 23.9 26.7 25.9 25.6 23.4 24.5
19 ""                       <NA>                         

Nice! That looks better.

For the year data we would like to try splitting the strings for each row into different columns based on a space. Currently all the data is listed in one column called Years.

We can use the separate() function of the tidyr package again to do this. This will allow us to split the rows by spaces, as well as provide names for the new columns.

major_groups %<>%
  tidyr::separate(col = Years, 
                  into = c("2008", "2010", "2012", "2014", "2016", "2017"), 
                  sep = " ")

major_groups
# A tibble: 19 x 7
   Group                    `2008` `2010` `2012` `2014` `2016` `2017`
   <chr>                    <chr>  <chr>  <chr>  <chr>  <chr>  <chr> 
 1 "United States"          12.6   14.7   14.1   13.2   11.7   11.5  
 2 "Male"                   12.3   15.2   14.5   13.3   12.1   11.8  
 3 "Female"                 12.9   14.1   13.7   13.0   11.2   11.1  
 4 "Asian"                  7.1    8.5    78     79     6.6    6.6   
 5 "Asian Male"             6.3    8.3    74     7.2    6.7    6.5   
 6 "Asian Female"           7.9    8.6    8.1    8.6    6.6    6.7   
 7 "White"                  9.7    11.7   11.2   10.8   9.7    9.4   
 8 "White Male"             9.5    12.3   11.5   10.8   10.0   9.6   
 9 "White Female"           10.0   11.1   10.8   10.7   9.4    9.1   
10 "Latino"                 16.7   18.5   17.3   15.2   13.7   13.2  
11 "Latino Male"            13.6   16.8   16.0   14.0   12.6   12.4  
12 "Latina Female"          20.2   20.3   18.8   16.5   14.8   13.9  
13 "Black"                  20.4   22.5   22.4   20.6   17.2   17.9  
14 "Black Male"             23.7   26.0   25.6   23.5   20.1   20.8  
15 "Black Female"           17.0   19.0   19.3   17.6   14.2   14.8  
16 "Native American"        24.4   28.8   27.0   26.3   25.8   23.9  
17 "Native American Male"   25.0   30.9   28.0   26.9   28.1   23.3  
18 "Native American Female" 23.9   26.7   25.9   25.6   23.4   24.5  
19 ""                       <NA>   <NA>   <NA>   <NA>   <NA>   <NA>  

Looks pretty good!

We appear to have an empty row at the very end. Since all the values are NA, we can use the drop_na() function of the tidyr package to remove it.

major_groups %<>%
  tidyr::drop_na()

major_groups
# A tibble: 18 x 7
   Group                  `2008` `2010` `2012` `2014` `2016` `2017`
   <chr>                  <chr>  <chr>  <chr>  <chr>  <chr>  <chr> 
 1 United States          12.6   14.7   14.1   13.2   11.7   11.5  
 2 Male                   12.3   15.2   14.5   13.3   12.1   11.8  
 3 Female                 12.9   14.1   13.7   13.0   11.2   11.1  
 4 Asian                  7.1    8.5    78     79     6.6    6.6   
 5 Asian Male             6.3    8.3    74     7.2    6.7    6.5   
 6 Asian Female           7.9    8.6    8.1    8.6    6.6    6.7   
 7 White                  9.7    11.7   11.2   10.8   9.7    9.4   
 8 White Male             9.5    12.3   11.5   10.8   10.0   9.6   
 9 White Female           10.0   11.1   10.8   10.7   9.4    9.1   
10 Latino                 16.7   18.5   17.3   15.2   13.7   13.2  
11 Latino Male            13.6   16.8   16.0   14.0   12.6   12.4  
12 Latina Female          20.2   20.3   18.8   16.5   14.8   13.9  
13 Black                  20.4   22.5   22.4   20.6   17.2   17.9  
14 Black Male             23.7   26.0   25.6   23.5   20.1   20.8  
15 Black Female           17.0   19.0   19.3   17.6   14.2   14.8  
16 Native American        24.4   28.8   27.0   26.3   25.8   23.9  
17 Native American Male   25.0   30.9   28.0   26.9   28.1   23.3  
18 Native American Female 23.9   26.7   25.9   25.6   23.4   24.5  

Great, now we have 18 rows.

It’s important to look very carefully at the text. Again, there are some values missing a decimal place. For example the row where the Group value is Asian, the third and fourth values are missing a decimal place.

Looking at the original table we see that even values like 10 are represented as 10.0.

We can also see that the number values are of class character based on the <chr> at the top of each column.


Click here for an explanation about data types in R and about character strings.

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).


To fix this, we will first remove all decimals by taking advantage of the fact that these values are character strings (this is also sort of like multiplying all values that do have a decimal by 10). Next we will convert the values to be numeric and finally we will multiply all values by .01 to add the decimals back.

Here, we use the mutate() function combined with the across() function, which allows us to specify the columns we want to perform a function on. We want to do this for all the year columns, so we can exclude the Group column by using a minus sign operator - in the .cols argument of the across() function like so: mutate(across(.cols = -Group))

Finally, we will use the str_remove() function of the stringr package to find instances of “.” and remove them. Since “.” is a regex, and indicates any character string, thus we need “\” to have R interpret a decimal or a period instead, as we can see from the RStudio cheat sheets:

To pass the data from all the columns except our Group variable into our str_remove() function, we need to use the . notation as a replacement for the data that we specified by the .colsargument and we need to use ~ in front of the function name.

major_groups %<>%
    mutate(across(.cols = -Group, 
                  ~str_remove(string = ., pattern = "\\.")))

major_groups
# A tibble: 18 x 7
   Group                  `2008` `2010` `2012` `2014` `2016` `2017`
   <chr>                  <chr>  <chr>  <chr>  <chr>  <chr>  <chr> 
 1 United States          126    147    141    132    117    115   
 2 Male                   123    152    145    133    121    118   
 3 Female                 129    141    137    130    112    111   
 4 Asian                  71     85     78     79     66     66    
 5 Asian Male             63     83     74     72     67     65    
 6 Asian Female           79     86     81     86     66     67    
 7 White                  97     117    112    108    97     94    
 8 White Male             95     123    115    108    100    96    
 9 White Female           100    111    108    107    94     91    
10 Latino                 167    185    173    152    137    132   
11 Latino Male            136    168    160    140    126    124   
12 Latina Female          202    203    188    165    148    139   
13 Black                  204    225    224    206    172    179   
14 Black Male             237    260    256    235    201    208   
15 Black Female           170    190    193    176    142    148   
16 Native American        244    288    270    263    258    239   
17 Native American Male   250    309    280    269    281    233   
18 Native American Female 239    267    259    256    234    245   

Great, now in order to multiply each value by 0.1 we need to first make the values numeric. Currently we can tell that they are character strings based on the <char> values listed under each column name.

To convert our values to be numeric we can use the base as.numeric() function. Again we will use mutate() and across(). Since this function doesn’t require any arguments, we don’t need to specify it’s input like we just did for the str_remove() but we could do so as shown below.

major_groups %<>%
   mutate(across(.cols = -Group, as.numeric))

#this is equivalent:

#major_groups %<>%
#   mutate(across(.cols = -Group, ~as.numeric(.)))

major_groups
# A tibble: 18 x 7
   Group                  `2008` `2010` `2012` `2014` `2016` `2017`
   <chr>                   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
 1 United States             126    147    141    132    117    115
 2 Male                      123    152    145    133    121    118
 3 Female                    129    141    137    130    112    111
 4 Asian                      71     85     78     79     66     66
 5 Asian Male                 63     83     74     72     67     65
 6 Asian Female               79     86     81     86     66     67
 7 White                      97    117    112    108     97     94
 8 White Male                 95    123    115    108    100     96
 9 White Female              100    111    108    107     94     91
10 Latino                    167    185    173    152    137    132
11 Latino Male               136    168    160    140    126    124
12 Latina Female             202    203    188    165    148    139
13 Black                     204    225    224    206    172    179
14 Black Male                237    260    256    235    201    208
15 Black Female              170    190    193    176    142    148
16 Native American           244    288    270    263    258    239
17 Native American Male      250    309    280    269    281    233
18 Native American Female    239    267    259    256    234    245

Great, we can see that the year variables are now numeric as they are now type double as indicated by the <dbl> below each column name. See the above section about data types if you are unfamiliar with type double.

OK, now we can multiply each value by 0.1 to add our decimal points back and get back to the original values.

major_groups %<>%
    mutate(across(.cols = -Group, ~ . * 0.1))

major_groups
# A tibble: 18 x 7
   Group                  `2008` `2010` `2012` `2014` `2016` `2017`
   <chr>                   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
 1 United States            12.6   14.7   14.1   13.2   11.7   11.5
 2 Male                     12.3   15.2   14.5   13.3   12.1   11.8
 3 Female                   12.9   14.1   13.7   13     11.2   11.1
 4 Asian                     7.1    8.5    7.8    7.9    6.6    6.6
 5 Asian Male                6.3    8.3    7.4    7.2    6.7    6.5
 6 Asian Female              7.9    8.6    8.1    8.6    6.6    6.7
 7 White                     9.7   11.7   11.2   10.8    9.7    9.4
 8 White Male                9.5   12.3   11.5   10.8   10      9.6
 9 White Female             10     11.1   10.8   10.7    9.4    9.1
10 Latino                   16.7   18.5   17.3   15.2   13.7   13.2
11 Latino Male              13.6   16.8   16     14     12.6   12.4
12 Latina Female            20.2   20.3   18.8   16.5   14.8   13.9
13 Black                    20.4   22.5   22.4   20.6   17.2   17.9
14 Black Male               23.7   26     25.6   23.5   20.1   20.8
15 Black Female             17     19     19.3   17.6   14.2   14.8
16 Native American          24.4   28.8   27     26.3   25.8   23.9
17 Native American Male     25     30.9   28     26.9   28.1   23.3
18 Native American Female   23.9   26.7   25.9   25.6   23.4   24.5

Now is a good time to double check that our table looks like what we expect.

Looks good!

We also want to add a couple of variables about Race_Ethnicity and Gender so that we can select across groups later. We can use the recode() function of the dplyr package to change specific values, as we create a new Race_Ethnicity variable from the Group variable. For the Data for all of the US we want the Race_Ethnicity variable values to be "All_races".

major_groups %<>% 
  mutate(Race_Ethnicity = 
           recode(Group, "United States" = "All_races",
                                "Female" = "All_races",
                                  "Male" = "All_races"))

head(major_groups)
# A tibble: 6 x 8
  Group         `2008` `2010` `2012` `2014` `2016` `2017` Race_Ethnicity
  <chr>          <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>         
1 United States   12.6   14.7   14.1   13.2   11.7   11.5 All_races     
2 Male            12.3   15.2   14.5   13.3   12.1   11.8 All_races     
3 Female          12.9   14.1   13.7   13     11.2   11.1 All_races     
4 Asian            7.1    8.5    7.8    7.9    6.6    6.6 Asian         
5 Asian Male       6.3    8.3    7.4    7.2    6.7    6.5 Asian Male    
6 Asian Female     7.9    8.6    8.1    8.6    6.6    6.7 Asian Female  

We also want to remove Male and Female from this “Race_Ethnicity” variable, We can do so using the str_remove() function of the stringr package. Importantly, we are also removing the space before “Female” and "Male.

major_groups %<>% 
  mutate(Race_Ethnicity = str_remove(string = Race_Ethnicity,  
                                    pattern = " Female| Male"))

head(major_groups)
# A tibble: 6 x 8
  Group         `2008` `2010` `2012` `2014` `2016` `2017` Race_Ethnicity
  <chr>          <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>         
1 United States   12.6   14.7   14.1   13.2   11.7   11.5 All_races     
2 Male            12.3   15.2   14.5   13.3   12.1   11.8 All_races     
3 Female          12.9   14.1   13.7   13     11.2   11.1 All_races     
4 Asian            7.1    8.5    7.8    7.9    6.6    6.6 Asian         
5 Asian Male       6.3    8.3    7.4    7.2    6.7    6.5 Asian         
6 Asian Female     7.9    8.6    8.1    8.6    6.6    6.7 Asian         

For the new Gender variable we would like to extract just the “Female” and “Male” text from the Group variable. The str_extract() function of the stringr package will do this, and it will give us an NA value for any rows where “Female” or “Male” were not present. We can then replace the NA values with the text “All” to represent the total value for both male and female using the replace_na() function of the tidyr() package.

major_groups %<>% 
  mutate(Gender = str_extract(string = Group, 
                              pattern = "Female|Male")) %>%
  mutate(Gender = replace_na(Gender, replace = "All"))

head(major_groups)
# A tibble: 6 x 9
  Group         `2008` `2010` `2012` `2014` `2016` `2017` Race_Ethnicity Gender
  <chr>          <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>          <chr> 
1 United States   12.6   14.7   14.1   13.2   11.7   11.5 All_races      All   
2 Male            12.3   15.2   14.5   13.3   12.1   11.8 All_races      Male  
3 Female          12.9   14.1   13.7   13     11.2   11.1 All_races      Female
4 Asian            7.1    8.5    7.8    7.9    6.6    6.6 Asian          All   
5 Asian Male       6.3    8.3    7.4    7.2    6.7    6.5 Asian          Male  
6 Asian Female     7.9    8.6    8.1    8.6    6.6    6.7 Asian          Female

We would also like to replace Latino and Latina with Latinx. We can use another stringr function for this. This function, str_replace() allows us to remove and replace a particular pattern.

Question Opportunity

What other tidyverse functions could you use to replace Latino and Latina with Latinx. Why might the str_replace() function be preferable?


Click here to reveal the answer.

While the recode() function will work in this case, the str_replace() function can be used more simply, as it will look for the pattern of Latino and Latina to replace even if that pattern is part of a value that has more characters. In contrast, the recode and case_when() functions would require that Latinx be specified for the replacement for both Latino and Latina separately, as well as the more complicated replacements for the compound groups like Latinx Male for Latino Male. This is longer and thus creates more opportunities for a mistake.

You could also use the case_when() function of the dplyr package if you are familiar with it. However, this would have the same issues as the recode() function in that it replaces values not patterns. In addition, you would need to make sure that the other values of the Race_Ethnicity and Group variables were not replaced with NA.

major_groups %>% 
  mutate(Race_Ethnicity = 
           recode(Race_Ethnicity, "Latino" = "Latinx",
                                  "Latina" = "Latinx"),
          Group = 
           recode(Group, "Latino" = "Latinx",
                    "Latino Male" = "Latinx Male", 
                  "Latina Female" = "Latinx Female"))
# A tibble: 18 x 9
   Group         `2008` `2010` `2012` `2014` `2016` `2017` Race_Ethnicity Gender
   <chr>          <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>          <chr> 
 1 United States   12.6   14.7   14.1   13.2   11.7   11.5 All_races      All   
 2 Male            12.3   15.2   14.5   13.3   12.1   11.8 All_races      Male  
 3 Female          12.9   14.1   13.7   13     11.2   11.1 All_races      Female
 4 Asian            7.1    8.5    7.8    7.9    6.6    6.6 Asian          All   
 5 Asian Male       6.3    8.3    7.4    7.2    6.7    6.5 Asian          Male  
 6 Asian Female     7.9    8.6    8.1    8.6    6.6    6.7 Asian          Female
 7 White            9.7   11.7   11.2   10.8    9.7    9.4 White          All   
 8 White Male       9.5   12.3   11.5   10.8   10      9.6 White          Male  
 9 White Female    10     11.1   10.8   10.7    9.4    9.1 White          Female
10 Latinx          16.7   18.5   17.3   15.2   13.7   13.2 Latinx         All   
11 Latinx Male     13.6   16.8   16     14     12.6   12.4 Latinx         Male  
12 Latinx Female   20.2   20.3   18.8   16.5   14.8   13.9 Latinx         Female
13 Black           20.4   22.5   22.4   20.6   17.2   17.9 Black          All   
14 Black Male      23.7   26     25.6   23.5   20.1   20.8 Black          Male  
15 Black Female    17     19     19.3   17.6   14.2   14.8 Black          Female
16 Native Ameri~   24.4   28.8   27     26.3   25.8   23.9 Native Americ~ All   
17 Native Ameri~   25     30.9   28     26.9   28.1   23.3 Native Americ~ Male  
18 Native Ameri~   23.9   26.7   25.9   25.6   23.4   24.5 Native Americ~ Female

major_groups %<>%
  mutate(across(.cols = c(Group, Race_Ethnicity), 
                ~str_replace(string = ., 
                            pattern = "Latino|Latina",
                        replacement = "Latinx")))
major_groups
# A tibble: 18 x 9
   Group         `2008` `2010` `2012` `2014` `2016` `2017` Race_Ethnicity Gender
   <chr>          <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>          <chr> 
 1 United States   12.6   14.7   14.1   13.2   11.7   11.5 All_races      All   
 2 Male            12.3   15.2   14.5   13.3   12.1   11.8 All_races      Male  
 3 Female          12.9   14.1   13.7   13     11.2   11.1 All_races      Female
 4 Asian            7.1    8.5    7.8    7.9    6.6    6.6 Asian          All   
 5 Asian Male       6.3    8.3    7.4    7.2    6.7    6.5 Asian          Male  
 6 Asian Female     7.9    8.6    8.1    8.6    6.6    6.7 Asian          Female
 7 White            9.7   11.7   11.2   10.8    9.7    9.4 White          All   
 8 White Male       9.5   12.3   11.5   10.8   10      9.6 White          Male  
 9 White Female    10     11.1   10.8   10.7    9.4    9.1 White          Female
10 Latinx          16.7   18.5   17.3   15.2   13.7   13.2 Latinx         All   
11 Latinx Male     13.6   16.8   16     14     12.6   12.4 Latinx         Male  
12 Latinx Female   20.2   20.3   18.8   16.5   14.8   13.9 Latinx         Female
13 Black           20.4   22.5   22.4   20.6   17.2   17.9 Black          All   
14 Black Male      23.7   26     25.6   23.5   20.1   20.8 Black          Male  
15 Black Female    17     19     19.3   17.6   14.2   14.8 Black          Female
16 Native Ameri~   24.4   28.8   27     26.3   25.8   23.9 Native Americ~ All   
17 Native Ameri~   25     30.9   28     26.9   28.1   23.3 Native Americ~ Male  
18 Native Ameri~   23.9   26.7   25.9   25.6   23.4   24.5 Native Americ~ Female

Finally, we would like to change the shape of our table so that we have a new column that represents the year and a new column that represents the value for that year.

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 of the tidyr package to change the shape of our table.

There are 3 main arguments in this function:

  1. cols - specifies what columns to collapse
  2. names_to - 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 - 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 chose those that contain the string "20" using the contains() function of dplyr.

major_groups_long <- major_groups %>%
  tidyr::pivot_longer(cols = contains("20"),
                  names_to = "Year",
                 values_to = "Percent",
              names_prefix = "Perc_") %>%
  dplyr::mutate(Year = as.numeric(Year))

major_groups_long
# A tibble: 108 x 5
   Group         Race_Ethnicity Gender  Year Percent
   <chr>         <chr>          <chr>  <dbl>   <dbl>
 1 United States All_races      All     2008    12.6
 2 United States All_races      All     2010    14.7
 3 United States All_races      All     2012    14.1
 4 United States All_races      All     2014    13.2
 5 United States All_races      All     2016    11.7
 6 United States All_races      All     2017    11.5
 7 Male          All_races      Male    2008    12.3
 8 Male          All_races      Male    2010    15.2
 9 Male          All_races      Male    2012    14.5
10 Male          All_races      Male    2014    13.3
# ... with 98 more rows

Excellent, now we need to do the same for the other two tables on this page:

Asian Subgroups 2017


Here was the text from our first image of this table:

asian_sub_2017
[1] "United States 11.5\nMale 11.8\nFemale 11.1\nASIAN 6.8\nAsian Male 65\nAsian Female 67\nCHINESE 43\nChinese Male AT\nChinese Female 3.9\nVIETNAMESE 55\nVietnamese Male 75\nVietnamese Female 3.4\nINDIAN 5.9\nIndian Male 4.1\nIndian Female 78\nPAKISTANI 64\nPakistani Male\n\nPakistani Female\n\nKOREAN 65\nKorean Male 8.0\nKorean Female 5.0\nTWO OR MORE 68\nTwo or More Male\n\nTwo or More Female\nFILIPINO. S\231~\231~S~—~SS\nFilipino Male 65\nFilipino Female 81\nHMONG 14.0\nHmong Male 18.6\n"

Now we again want to split the data into rows based on the newline regex. This is something we will continue to do for all the tables.

One option is to copy and paste code we wrote above each time. However, this is not very efficient and is error prone. Alternatively, we can create an R function to accomplish this succinctly. Functions allow us to perform the same process on multiple data inputs. See this other case study for more details about how to write a function.

In general, the process of writing functions involves first specifying an input that is used within the function to create an output. In this case, the data input is text which will be replaced by the actual image text that we are working on, and then used in the subsequent steps to wrangle the data. We will call our function make_rows().

make_rows <- function(text){
  text %>%
  str_split("\n") %>%
  unlist() %>%
  as_tibble()
}

Great! Now let’s apply our function to the asian_sub_2017 data!

asian_sub_2017 <- make_rows(asian_sub_2017) 

# Scroll through the output!
print(asian_sub_2017, n = 50)
# A tibble: 33 x 1
   value                  
   <chr>                  
 1 "United States 11.5"   
 2 "Male 11.8"            
 3 "Female 11.1"          
 4 "ASIAN 6.8"            
 5 "Asian Male 65"        
 6 "Asian Female 67"      
 7 "CHINESE 43"           
 8 "Chinese Male AT"      
 9 "Chinese Female 3.9"   
10 "VIETNAMESE 55"        
11 "Vietnamese Male 75"   
12 "Vietnamese Female 3.4"
13 "INDIAN 5.9"           
14 "Indian Male 4.1"      
15 "Indian Female 78"     
16 "PAKISTANI 64"         
17 "Pakistani Male"       
18 ""                     
19 "Pakistani Female"     
20 ""                     
21 "KOREAN 65"            
22 "Korean Male 8.0"      
23 "Korean Female 5.0"    
24 "TWO OR MORE 68"       
25 "Two or More Male"     
26 ""                     
27 "Two or More Female"   
28 "FILIPINO. S™~™~S~—~SS"
29 "Filipino Male 65"     
30 "Filipino Female 81"   
31 "HMONG 14.0"           
32 "Hmong Male 18.6"      
33 ""                     

As you can see, there are some strange values for some of the rows. For example the row that starts with CHINESE MAle has AT percentage of disconnected youth, and the row that should say FILIPINO says S™S™~SS.

[[1]]
[1] "Chinese" "Male"    "AT"     
[[1]]
[1] "FILIPINO."   "S\231~\231~S~—~SS"

Notice how there are empty rows. According to the report (PDF), these spaces are empty to denote that the estimates were unreliable for these groups. Since we don’t really need these rows as they are missing values anyway and because images with less text that of higher resolution are often easier for the OCR to interpret correctly, we will now try using three images of this table rather than just one. This allows us to zoom in on the parts of the table that we actually want and hopefully, the OCR will be able to interpret the text correctly.

So we will now import and extract text from three screenshots of this table where we stop just after the row that starts with PAKISTANI in the first image, and then an image of the Korean rows up to the next row with no values, and finally an image starting at the row that starts with FILIPNO. Thus all of the empty rows will not be included.

These images are called asian_sub_2017_A, asian_sub_2017_B, and asian_sub_2017_C.

Now we will apply our make_rows() function to the text from these images.

asian_sub_2017_A<- make_rows(asian_sub_2017_A)
asian_sub_2017_B <- make_rows(asian_sub_2017_B)
asian_sub_2017_C <- make_rows(asian_sub_2017_C)

asian_sub_2017_A 
# A tibble: 17 x 1
   value                  
   <chr>                  
 1 "United States 11.5"   
 2 "Male 11.8"            
 3 "Female 11.1"          
 4 "ASIAN 6.6"            
 5 "Asian Male 6.5"       
 6 "Asian Female 6.7"     
 7 "CHINESE 4.3"          
 8 "Chinese Male 4.7"     
 9 "Chinese Female 3.9"   
10 "VIETNAMESE 5.5"       
11 "Vietnamese Male 75"   
12 "Vietnamese Female 3.4"
13 "INDIAN 5.9"           
14 "Indian Male 4.1"      
15 "Indian Female 7.8"    
16 "PAKISTANI 6.4"        
17 ""                     
asian_sub_2017_B
# A tibble: 5 x 1
  value              
  <chr>              
1 "KOREAN 6.5"       
2 "Korean Male 8.0"  
3 "Korean Female 5.0"
4 "TWO OR MORE 6.6"  
5 ""                 
asian_sub_2017_C
# A tibble: 6 x 1
  value                
  <chr>                
1 "FILIPINO 7.3"       
2 "Filipino Male 6.5"  
3 "Filipino Female 8.1"
4 "HMONG 14.0"         
5 "Hmong Male 18.6"    
6 ""                   

Much better!

We can now combine the objects with the bind_rows() function of the dplyr package, which will append each of these tibbles together one after the other.

asian_sub_2017 <-bind_rows(asian_sub_2017_A, 
                           asian_sub_2017_B,
                           asian_sub_2017_C)

asian_sub_2017
# A tibble: 28 x 1
   value             
   <chr>             
 1 United States 11.5
 2 Male 11.8         
 3 Female 11.1       
 4 ASIAN 6.6         
 5 Asian Male 6.5    
 6 Asian Female 6.7  
 7 CHINESE 4.3       
 8 Chinese Male 4.7  
 9 Chinese Female 3.9
10 VIETNAMESE 5.5    
# ... with 18 more rows

Looks pretty good!

Now we have similar wrangling steps to perform as we did previously and we will need to do the same for the Latinx subgroups table. So it is a good idea to make another function.

Even though we appear to have all of the decimal places for the values, we will include this in our function, just to make sure the data is correct.

Question Opportunity

Can you explain what each of the commands are doing within the function?

clean_table <- function(table){
  table %>%
    separate(., col = value,
               into = c("Group", "Percentage"),
                sep =  "(?<=[[:alpha:]])\\s(?=[0-9])") %>% 
    drop_na() %>%
    mutate(Group = str_to_title(Group)) %>%
    mutate(Percentage = str_remove(string = Percentage,
                                   pattern = "\\.")) %>%
    separate(Percentage, c("Percent"), sep = " ") %>%
    mutate(Percent = as.numeric(Percent)) %>%
    mutate(Percent = Percent * 0.1) %>%
    mutate(Race_Ethnicity = recode(Group, 
                  "United States" = "All_races", 
                  "Female" = "All_races",
                  "Male" = "All_races")) %>%
    mutate(Race_Ethnicity = str_remove(string = Race_Ethnicity,  
                                       pattern = " Female| Male")) %>%
    mutate(Gender = str_extract(string = Group,
                                pattern ="Female|Male")) %>%
    mutate(Gender = replace_na(Gender, replace = "All"))
 
}
asian_sub_2017 <- clean_table(table = asian_sub_2017)
asian_sub_2017
# A tibble: 25 x 4
   Group          Percent Race_Ethnicity Gender
   <chr>            <dbl> <chr>          <chr> 
 1 United States     11.5 All_races      All   
 2 Male              11.8 All_races      Male  
 3 Female            11.1 All_races      Female
 4 Asian              6.6 Asian          All   
 5 Asian Male         6.5 Asian          Male  
 6 Asian Female       6.7 Asian          Female
 7 Chinese            4.3 Chinese        All   
 8 Chinese Male       4.7 Chinese        Male  
 9 Chinese Female     3.9 Chinese        Female
10 Vietnamese         5.5 Vietnamese     All   
# ... with 15 more rows

Great! This looks as we expected.

Question Opportunity

Why do we not need to use pivot_longer() with this data?

Latinx Subgroups 2017


Recall that this is the table we want to wrangle:

knitr::include_graphics(here::here("img", "latinx2017.png"))

Question Opportunity

Do you notice anything incorrect about this table? It’s always important to be pay attention to the data even when it is in raw format.


Click here to see what is incorrect.

The last two rows both say “Female”.


Sometimes when wrangling text data, we will come across a typo. We need to determine how to respond to the typo and make note of it. It’s often best to consult a secondary source to confirm that changes made are accurate.

For the purposes of this case study, we will assume that the first of the two rows represents male disconnection rates in the Latino/a subgroup; this would be consistent with the ordering of genders in the previous subgroups.

We will make sure to correct this typo when we can.

First let’s take a look of the text from the images we imported:

latinx_sub_2017_A
[1] "LATINO 13.2\nLatino Male 12.4\nLatina Female 13.9\n"
latinx_sub_2017_B
[1] "SOUTH AMERICAN 8.4\nSouth American Male 9.1\nSouth American Female 7.7\nCENTRAL AMERICAN 12.0\nCentral American Male 9.3\nCentral American Female 15.0\nMEXICAN 13.3\nMexican Male 12.2\nMexican Female 14.4\nOTHER LATINO 13.6\nOther Latino Male 15.3\nOther Latina Female 11.5\n"
latinx_sub_2017_C
[1] "PUERTO RICAN, DOMINICAN, CUBAN 15.1\nPR, DR, Cuban Female 15.7\nPR, DR, Cuban Female 14.4\n"

We can combine the strings together using the str_c() function (which stands for string collapse) of the stringr package.

latinx_sub_2017 <- stringr::str_c(latinx_sub_2017_A, latinx_sub_2017_B, latinx_sub_2017_C)

latinx_sub_2017
[1] "LATINO 13.2\nLatino Male 12.4\nLatina Female 13.9\nSOUTH AMERICAN 8.4\nSouth American Male 9.1\nSouth American Female 7.7\nCENTRAL AMERICAN 12.0\nCentral American Male 9.3\nCentral American Female 15.0\nMEXICAN 13.3\nMexican Male 12.2\nMexican Female 14.4\nOTHER LATINO 13.6\nOther Latino Male 15.3\nOther Latina Female 11.5\nPUERTO RICAN, DOMINICAN, CUBAN 15.1\nPR, DR, Cuban Female 15.7\nPR, DR, Cuban Female 14.4\n"

Now let’s correct that typo.

Question Opportunity

How might you do this?


Click here to reveal the code.
latinx_sub_2017 %<>% 
  str_replace(string = .,
             pattern = "DR, Cuban Female 15.7\nPR",
         replacement = "DR, Cuban Male 15.7\nPR")

latinx_sub_2017
[1] "LATINO 13.2\nLatino Male 12.4\nLatina Female 13.9\nSOUTH AMERICAN 8.4\nSouth American Male 9.1\nSouth American Female 7.7\nCENTRAL AMERICAN 12.0\nCentral American Male 9.3\nCentral American Female 15.0\nMEXICAN 13.3\nMexican Male 12.2\nMexican Female 14.4\nOTHER LATINO 13.6\nOther Latino Male 15.3\nOther Latina Female 11.5\nPUERTO RICAN, DOMINICAN, CUBAN 15.1\nPR, DR, Cuban Male 15.7\nPR, DR, Cuban Female 14.4\n"

Question Opportunity

Can you recall the commands within our make_rows() function to separate the data into rows and create a tibble?


Click here to reveal the code.
latinx_sub_2017 %<>%
  str_split("\n") %>%
  unlist() %>%
  as_tibble()

latinx_sub_2017
# A tibble: 19 x 1
   value                                
   <chr>                                
 1 "LATINO 13.2"                        
 2 "Latino Male 12.4"                   
 3 "Latina Female 13.9"                 
 4 "SOUTH AMERICAN 8.4"                 
 5 "South American Male 9.1"            
 6 "South American Female 7.7"          
 7 "CENTRAL AMERICAN 12.0"              
 8 "Central American Male 9.3"          
 9 "Central American Female 15.0"       
10 "MEXICAN 13.3"                       
11 "Mexican Male 12.2"                  
12 "Mexican Female 14.4"                
13 "OTHER LATINO 13.6"                  
14 "Other Latino Male 15.3"             
15 "Other Latina Female 11.5"           
16 "PUERTO RICAN, DOMINICAN, CUBAN 15.1"
17 "PR, DR, Cuban Male 15.7"            
18 "PR, DR, Cuban Female 14.4"          
19 ""                                   

Now we can apply our function.

latinx_sub_2017 <- clean_table(table = latinx_sub_2017)
latinx_sub_2017
# A tibble: 18 x 4
   Group                          Percent Race_Ethnicity                 Gender
   <chr>                            <dbl> <chr>                          <chr> 
 1 Latino                            13.2 Latino                         All   
 2 Latino Male                       12.4 Latino                         Male  
 3 Latina Female                     13.9 Latina                         Female
 4 South American                     8.4 South American                 All   
 5 South American Male                9.1 South American                 Male  
 6 South American Female              7.7 South American                 Female
 7 Central American                  12   Central American               All   
 8 Central American Male              9.3 Central American               Male  
 9 Central American Female           15   Central American               Female
10 Mexican                           13.3 Mexican                        All   
11 Mexican Male                      12.2 Mexican                        Male  
12 Mexican Female                    14.4 Mexican                        Female
13 Other Latino                      13.6 Other Latino                   All   
14 Other Latino Male                 15.3 Other Latino                   Male  
15 Other Latina Female               11.5 Other Latina                   Female
16 Puerto Rican, Dominican, Cuban    15.1 Puerto Rican, Dominican, Cuban All   
17 Pr, Dr, Cuban Male                15.7 Pr, Dr, Cuban                  Male  
18 Pr, Dr, Cuban Female              14.4 Pr, Dr, Cuban                  Female

It looks like we’ve successfully corrected the typo!

Let’s also replace the abbreviations for Puerto Rican and Dominican and let’s replace Latino/Latina with Latinx.

Question Opportunity

How might you do this using the str_replace() function?


Click here to reveal the code.
latinx_sub_2017 %<>%
  mutate(across(.cols = c(Group, Race_Ethnicity), 
           ~str_replace(string = .,
                       pattern = "Pr, Dr, Cuban",
                   replacement = "Puerto Rican, Dominican, Cuban")),
         across(.cols = c(Group,Race_Ethnicity),
          ~str_replace(string = .,
                      pattern = "Latino|Latina",
                  replacement = "Latinx")))

latinx_sub_2017
# A tibble: 18 x 4
   Group                                 Percent Race_Ethnicity           Gender
   <chr>                                   <dbl> <chr>                    <chr> 
 1 Latinx                                   13.2 Latinx                   All   
 2 Latinx Male                              12.4 Latinx                   Male  
 3 Latinx Female                            13.9 Latinx                   Female
 4 South American                            8.4 South American           All   
 5 South American Male                       9.1 South American           Male  
 6 South American Female                     7.7 South American           Female
 7 Central American                         12   Central American         All   
 8 Central American Male                     9.3 Central American         Male  
 9 Central American Female                  15   Central American         Female
10 Mexican                                  13.3 Mexican                  All   
11 Mexican Male                             12.2 Mexican                  Male  
12 Mexican Female                           14.4 Mexican                  Female
13 Other Latinx                             13.6 Other Latinx             All   
14 Other Latinx Male                        15.3 Other Latinx             Male  
15 Other Latinx Female                      11.5 Other Latinx             Female
16 Puerto Rican, Dominican, Cuban           15.1 Puerto Rican, Dominican~ All   
17 Puerto Rican, Dominican, Cuban Male      15.7 Puerto Rican, Dominican~ Male  
18 Puerto Rican, Dominican, Cuban Female    14.4 Puerto Rican, Dominican~ Female

Great!

Now we are ready to look at the data from 2018 for the Asian and Latinx subgroups from the other report.

Asian Subgroups 2018


Recall that this was the page with the table of interest for the Asian subgroups with 2018 data (published in 2020):

As you can see, the data for the subgroups is shown in the table but the overall data for Asians is located in the text.

We will use a screenshot of each to extract the data for this year.

Trial and error indicated that again dividing the table into multiple screenshots improved the text extraction. We saved the text from these images during the Data Import section as asian_sub_2018_A and asian_sub_2018_B.

asian_sub_2018_A
[1] "CHINESE : 4.1\nMen 4.5\nWomen : 3.7\n\nINDIAN 5.4\nMen : 4.7\nWomen : 6.1\n\nKOREAN : 5.5\nMen 5.6\nWomen 5.4\n"
asian_sub_2018_B
[1] "VIETNAMESE: 6.3\nMen 7.6\nWomen 5.0\n\nFILIPINO : 6.8\nMen : 63\nWomen 7.4\n\nHMONG : 10.2\n\nCAMBODIAN: 13.8\n"

Again, let collapse these together with str_c() function.

asian_sub_2018 <-str_c(asian_sub_2018_A, asian_sub_2018_B)

Now we can apply our make_rows() function:

asian_sub_2018 <- make_rows(asian_sub_2018)

asian_sub_2018
# A tibble: 23 x 1
   value          
   <chr>          
 1 "CHINESE : 4.1"
 2 "Men 4.5"      
 3 "Women : 3.7"  
 4 ""             
 5 "INDIAN 5.4"   
 6 "Men : 4.7"    
 7 "Women : 6.1"  
 8 ""             
 9 "KOREAN : 5.5" 
10 "Men 5.6"      
# ... with 13 more rows

First, we now have colons : in our table that we will want to separate by. Unfortunately, the text in each row isn’t extracted in the same way by the OCR. Some rows have only a space, while others have spaces around a colon; or for the row with VIETNAM we see a colon directly after the word followed by a space. Thus, we will modify our seperate() function with this change. We can specify that the separator between any letter and any digit should be either a space (\\s) or a colon with a space before and after it (\\s:\\s) using the or (|)operator.

So this will look like this:

asian_sub_2018 %>%
    separate(., col = value,
             into = c("Group", "Percent"), 
             sep =  "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])")
# A tibble: 23 x 2
   Group     Percent
   <chr>     <chr>  
 1 "CHINESE" 4.1    
 2 "Men"     4.5    
 3 "Women"   3.7    
 4 ""        <NA>   
 5 "INDIAN"  5.4    
 6 "Men"     4.7    
 7 "Women"   6.1    
 8 ""        <NA>   
 9 "KOREAN"  5.5    
10 "Men"     5.6    
# ... with 13 more rows

Then because of the row with VIETNAM, we will want to remove this colon using the str_remove() function like this:

asian_sub_2018 %>%
    separate(., col = value, 
               into = c("Group", "Percent"), 
                sep = "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])") %>% 
    mutate(Group= str_remove(string = Group, pattern = ":"))

The other difference from the previous function, is that we want to fill in a new Race_Ethnicity variable with the previous rows. We can do so by first replacing “Men” or “Women” which with the or operator is (“Men|Women”), with “missing”. Then we need to convert these to NA values using the na_if() function of the dplyr package, we just need to specify what column to modify and what value to change to NA. Finally we will then replace the NA values with the previous non-NA value using the fill() function of the tidyr package. Note that this does not work inside of the mutate() function. We just need to simply specify what column to modify and then the direction to replace values. In this case we want to replace in the downward direction using the previous values.

This will look like this:

asian_sub_2018 %>%
separate(., col = value, 
           into = c("Group", "Percent"), 
            sep = "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])") %>%
mutate(Group = str_remove(string = Group, pattern = ":")) %>%
mutate(Race_Ethnicity = str_replace(string = Group,
                                   pattern = "Men|Women", 
                               replacement = "missing")) %>%
head()
# A tibble: 6 x 3
  Group     Percent Race_Ethnicity
  <chr>     <chr>   <chr>         
1 "CHINESE" 4.1     "CHINESE"     
2 "Men"     4.5     "missing"     
3 "Women"   3.7     "missing"     
4 ""        <NA>    ""            
5 "INDIAN"  5.4     "INDIAN"      
6 "Men"     4.7     "missing"     
asian_sub_2018 %>%
separate(., col = value, 
           into = c("Group", "Percent"), 
            sep = "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])") %>%
mutate(Group = str_remove(string = Group, pattern = ":")) %>%
mutate(Race_Ethnicity = str_replace(string = Group,
                                   pattern = "Men|Women", 
                               replacement = "missing")) %>%
mutate(Race_Ethnicity = na_if(Race_Ethnicity, "missing")) %>%
head()
# A tibble: 6 x 3
  Group     Percent Race_Ethnicity
  <chr>     <chr>   <chr>         
1 "CHINESE" 4.1     "CHINESE"     
2 "Men"     4.5      <NA>         
3 "Women"   3.7      <NA>         
4 ""        <NA>    ""            
5 "INDIAN"  5.4     "INDIAN"      
6 "Men"     4.7      <NA>         
asian_sub_2018 %>%
separate(., col = value, 
           into = c("Group", "Percent"), 
            sep = "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])") %>%
mutate(Group = str_remove(string = Group, pattern = ":")) %>%
mutate(Race_Ethnicity = str_replace(string = Group,
                                   pattern = "Men|Women", 
                               replacement = "missing")) %>%
mutate(Race_Ethnicity = na_if(Race_Ethnicity, "missing")) %>%
fill(Race_Ethnicity, .direction = "down") %>%
head()
# A tibble: 6 x 3
  Group     Percent Race_Ethnicity
  <chr>     <chr>   <chr>         
1 "CHINESE" 4.1     "CHINESE"     
2 "Men"     4.5     "CHINESE"     
3 "Women"   3.7     "CHINESE"     
4 ""        <NA>    ""            
5 "INDIAN"  5.4     "INDIAN"      
6 "Men"     4.7     "INDIAN"      

OK! Now, let’s combine these pieces of our new function with the old pieces:

clean_table <- function(table){
  table %>%
    separate(., col = value, 
               into = c("Group", "Percent"), 
                sep =  "(?<=[[:alpha:]])\\s:\\s|\\s(?=[0-9])") %>% 
    mutate(Group= str_remove(string = Group, 
                            pattern = ":")) %>%
    drop_na() %>%
    mutate(Group = str_to_title(string = Group)) %>%
    mutate(Percent = str_remove(string = Percent, 
                               pattern = "\\.")) %>%
    mutate(Percent = as.numeric(Percent)) %>%
    mutate(Percent = Percent * 0.1) %>%
    mutate(Race_Ethnicity = str_replace(string = Group,
                                       pattern = "Men|Women", 
                                   replacement = "missing")) %>%
    mutate(Race_Ethnicity = na_if(Race_Ethnicity, "missing")) %>%
    fill(Race_Ethnicity, .direction = "down") %>%
    mutate(Gender = str_extract(string = Group, 
                               pattern = "Men|Women")) %>%
    mutate(Gender = replace_na(Gender, replace = "All"))
}
asian_sub_2018 <-clean_table(asian_sub_2018)
asian_sub_2018
# A tibble: 17 x 4
   Group      Percent Race_Ethnicity Gender
   <chr>        <dbl> <chr>          <chr> 
 1 Chinese        4.1 Chinese        All   
 2 Men            4.5 Chinese        Men   
 3 Women          3.7 Chinese        Women 
 4 Indian         5.4 Indian         All   
 5 Men            4.7 Indian         Men   
 6 Women          6.1 Indian         Women 
 7 Korean         5.5 Korean         All   
 8 Men            5.6 Korean         Men   
 9 Women          5.4 Korean         Women 
10 Vietnamese     6.3 Vietnamese     All   
11 Men            7.6 Vietnamese     Men   
12 Women          5   Vietnamese     Women 
13 Filipino       6.8 Filipino       All   
14 Men            6.3 Filipino       Men   
15 Women          7.4 Filipino       Women 
16 Hmong         10.2 Hmong          All   
17 Cambodian     13.8 Cambodian      All   

Looking good!

Now we just need to add the data for all Asians from the text.

We can do this using the add_row() function of the dplyr() package.

asian_sub_2018 %<>%
  add_row(Group = "Asian",
        Percent = 6.2,
 Race_Ethnicity = "Asian",
         Gender = "All") %>%
  add_row(Group = "Asian",
        Percent = 6.4,
 Race_Ethnicity = "Asian",
         Gender = "Men") %>%
  add_row(Group = "Asian",
        Percent = 6.1,
 Race_Ethnicity = "Asian",
         Gender = "Women")

asian_sub_2018
# A tibble: 20 x 4
   Group      Percent Race_Ethnicity Gender
   <chr>        <dbl> <chr>          <chr> 
 1 Chinese        4.1 Chinese        All   
 2 Men            4.5 Chinese        Men   
 3 Women          3.7 Chinese        Women 
 4 Indian         5.4 Indian         All   
 5 Men            4.7 Indian         Men   
 6 Women          6.1 Indian         Women 
 7 Korean         5.5 Korean         All   
 8 Men            5.6 Korean         Men   
 9 Women          5.4 Korean         Women 
10 Vietnamese     6.3 Vietnamese     All   
11 Men            7.6 Vietnamese     Men   
12 Women          5   Vietnamese     Women 
13 Filipino       6.8 Filipino       All   
14 Men            6.3 Filipino       Men   
15 Women          7.4 Filipino       Women 
16 Hmong         10.2 Hmong          All   
17 Cambodian     13.8 Cambodian      All   
18 Asian          6.2 Asian          All   
19 Asian          6.4 Asian          Men   
20 Asian          6.1 Asian          Women 

OK, now we just want to combine the 2018 data and the 2017 data for the Asian subgroups.

First let’s add a variable for year to both. Using mutate() we can add a variable Year where all values are 2017 like so:

asian_sub_2017 %<>%
  mutate(Year = 2017)
asian_sub_2017 
# A tibble: 25 x 5
   Group          Percent Race_Ethnicity Gender  Year
   <chr>            <dbl> <chr>          <chr>  <dbl>
 1 United States     11.5 All_races      All     2017
 2 Male              11.8 All_races      Male    2017
 3 Female            11.1 All_races      Female  2017
 4 Asian              6.6 Asian          All     2017
 5 Asian Male         6.5 Asian          Male    2017
 6 Asian Female       6.7 Asian          Female  2017
 7 Chinese            4.3 Chinese        All     2017
 8 Chinese Male       4.7 Chinese        Male    2017
 9 Chinese Female     3.9 Chinese        Female  2017
10 Vietnamese         5.5 Vietnamese     All     2017
# ... with 15 more rows
asian_sub_2018 %<>%
  mutate(Year = 2018)
asian_sub_2018
# A tibble: 20 x 5
   Group      Percent Race_Ethnicity Gender  Year
   <chr>        <dbl> <chr>          <chr>  <dbl>
 1 Chinese        4.1 Chinese        All     2018
 2 Men            4.5 Chinese        Men     2018
 3 Women          3.7 Chinese        Women   2018
 4 Indian         5.4 Indian         All     2018
 5 Men            4.7 Indian         Men     2018
 6 Women          6.1 Indian         Women   2018
 7 Korean         5.5 Korean         All     2018
 8 Men            5.6 Korean         Men     2018
 9 Women          5.4 Korean         Women   2018
10 Vietnamese     6.3 Vietnamese     All     2018
11 Men            7.6 Vietnamese     Men     2018
12 Women          5   Vietnamese     Women   2018
13 Filipino       6.8 Filipino       All     2018
14 Men            6.3 Filipino       Men     2018
15 Women          7.4 Filipino       Women   2018
16 Hmong         10.2 Hmong          All     2018
17 Cambodian     13.8 Cambodian      All     2018
18 Asian          6.2 Asian          All     2018
19 Asian          6.4 Asian          Men     2018
20 Asian          6.1 Asian          Women   2018

You may notice that Gender is coded differently for the two years. Let’s make this consistent now:

asian_sub_2018 %<>%
  mutate(across(.cols = c(Gender, Group),
               ~ str_replace(string = ., 
                            pattern = "Men", 
                        replacement = "Male")),
         across(.cols = c(Gender, Group),
               ~ str_replace(string = ., 
                            pattern = "Women", 
                        replacement = "Female")))

We can combine these two tibbles using the bind_rows() function of dplyr.

asian_subgroups <- bind_rows(asian_sub_2017, asian_sub_2018)

Notice that there are some cases where we only have one value for a particular group. For example, there are no male or female values for the Pakistani data.

We would like to have NA values for the comparable years/genders that are possible. We can fill out the rest of the table with NA values by performing the pivot_wider() and pivot_longer() functions sequentially like so:

asian_subgroups %<>% 
select(-Group) %>%
pivot_wider(names_from = Year, values_from = Percent) %>%
pivot_longer(cols = -c(Race_Ethnicity, Gender), 
names_to ="Year" , 
values_to="Percent")

Click here to see how this interactive table was created

This table was created using the datatable() function of the DT package.

Like so:

DT::datatable(asian_subgroups)

Great, now we are ready to perform similar wrangling for the Latinx subgroups.

Latinx Subgroups 2018


Recall that this was the page with the table of interest for the Latinx subgroup 2018 data:

In this case only a single image was needed. We previously imported the text form this image and called it latinx_sub_2018:

latinx_sub_2018
[1] "SOUTH :\n\nAMERICAN : 8.0\nMen 7.5\nMEXICAN : 12.9\nMen 12.0\nWomen 13.8\nPR, DR, CUBAN 13.7\nMen 14.9\nWomen 12.4\nCENTRAL :\n\nAMERICAN : 13.7\nMen 11.8\nWomen : 15.9\n"

Let’s first combine the South and Central American labels. Notice that there are multiple new line expressions in between and we don’t see repeated \n characters elsewhere. We can replace the pattern of exactly two \n (using \n{2} to specify exactly 2) or two newline regex with a space and colon in front with a single space.

latinx_sub_2018 <- str_replace_all(string = latinx_sub_2018, 
                                  pattern = "\\s:\n{2}|\n{2}",                                               replacement = " ")
latinx_sub_2018 
[1] "SOUTH AMERICAN : 8.0\nMen 7.5\nMEXICAN : 12.9\nMen 12.0\nWomen 13.8\nPR, DR, CUBAN 13.7\nMen 14.9\nWomen 12.4\nCENTRAL AMERICAN : 13.7\nMen 11.8\nWomen : 15.9\n"

Now we can apply our make_rows() and clean_table() functions:

latinx_sub_2018 <- make_rows(latinx_sub_2018 )

latinx_sub_2018
# A tibble: 12 x 1
   value                    
   <chr>                    
 1 "SOUTH AMERICAN : 8.0"   
 2 "Men 7.5"                
 3 "MEXICAN : 12.9"         
 4 "Men 12.0"               
 5 "Women 13.8"             
 6 "PR, DR, CUBAN 13.7"     
 7 "Men 14.9"               
 8 "Women 12.4"             
 9 "CENTRAL AMERICAN : 13.7"
10 "Men 11.8"               
11 "Women : 15.9"           
12 ""                       
latinx_sub_2018 <-clean_table(latinx_sub_2018)

latinx_sub_2018
# A tibble: 11 x 4
   Group            Percent Race_Ethnicity   Gender
   <chr>              <dbl> <chr>            <chr> 
 1 South American       8   South American   All   
 2 Men                  7.5 South American   Men   
 3 Mexican             12.9 Mexican          All   
 4 Men                 12   Mexican          Men   
 5 Women               13.8 Mexican          Women 
 6 Pr, Dr, Cuban       13.7 Pr, Dr, Cuban    All   
 7 Men                 14.9 Pr, Dr, Cuban    Men   
 8 Women               12.4 Pr, Dr, Cuban    Women 
 9 Central American    13.7 Central American All   
10 Men                 11.8 Central American Men   
11 Women               15.9 Central American Women 

Again we will replace Pr, Dr, Cuban:

latinx_sub_2018 %<>%
  mutate(Group = 
           str_replace(string = Group,
                      pattern = "Pr, Dr, Cuban",
                  replacement = "Puerto Rican, Dominican, Cuban"), 
          Race_Ethnicity = 
           str_replace(string = Race_Ethnicity,
                      pattern = "Pr, Dr, Cuban",
                  replacement = "Puerto Rican, Dominican, Cuban"))

We also want to add the total Latinx values according to the text:

knitr::include_graphics(here::here("img", "latinx_youth_excerpt.png"))

Question Opportunity

Can you recall how to add additional rows?


Click here to reveal the code.
latinx_sub_2018 %<>%
  add_row(Group = "Latinx",
        Percent = 12.8,
 Race_Ethnicity = "Latinx",
         Gender = "All") %>%
  add_row(Group = "Latinx",
        Percent = 12.3,
 Race_Ethnicity = "Latinx",
         Gender = "Men") %>%
  add_row(Group = "Latinx",
        Percent = 13.3,
 Race_Ethnicity = "Latinx",
         Gender = "Women")

tail(latinx_sub_2018)
# A tibble: 6 x 4
  Group            Percent Race_Ethnicity   Gender
  <chr>              <dbl> <chr>            <chr> 
1 Central American    13.7 Central American All   
2 Men                 11.8 Central American Men   
3 Women               15.9 Central American Women 
4 Latinx              12.8 Latinx           All   
5 Latinx              12.3 Latinx           Men   
6 Latinx              13.3 Latinx           Women 

And now we will recode gender like before to be consistent:

latinx_sub_2018 %<>%
  mutate(across(.cols = c(Gender, Group),
                ~ str_replace(string = ., 
                             pattern = "Men", 
                         replacement = "Male")),
         across(.cols = c(Gender, Group),
                ~ str_replace(string = ., 
                             pattern = "Women", 
                         replacement = "Female")))

head(latinx_sub_2018)
# A tibble: 6 x 4
  Group                          Percent Race_Ethnicity                 Gender
  <chr>                            <dbl> <chr>                          <chr> 
1 South American                     8   South American                 All   
2 Male                               7.5 South American                 Male  
3 Mexican                           12.9 Mexican                        All   
4 Male                              12   Mexican                        Male  
5 Female                            13.8 Mexican                        Female
6 Puerto Rican, Dominican, Cuban    13.7 Puerto Rican, Dominican, Cuban All   

Now we just need to combine all the data for the Latinx subgroups.

Again, first we will add a year variable to both the 2017 and 2018 data.

latinx_sub_2017 %<>%
  mutate(Year = 2017)

latinx_sub_2018 %<>%
  mutate(Year = 2018)

latinx_subgroups <- bind_rows(latinx_sub_2017, latinx_sub_2018)
latinx_subgroups
# A tibble: 32 x 5
   Group                   Percent Race_Ethnicity   Gender  Year
   <chr>                     <dbl> <chr>            <chr>  <dbl>
 1 Latinx                     13.2 Latinx           All     2017
 2 Latinx Male                12.4 Latinx           Male    2017
 3 Latinx Female              13.9 Latinx           Female  2017
 4 South American              8.4 South American   All     2017
 5 South American Male         9.1 South American   Male    2017
 6 South American Female       7.7 South American   Female  2017
 7 Central American           12   Central American All     2017
 8 Central American Male       9.3 Central American Male    2017
 9 Central American Female    15   Central American Female  2017
10 Mexican                    13.3 Mexican          All     2017
# ... with 22 more rows

Again, we would like to have NA values for the comparable years/genders that are possible. We will fill out the rest of the table with NA values by performing the pivot_wider() and pivot_longer() functions sequentially like so:

latinx_subgroups %<>%
  select(-Group) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to ="Year" , 
               values_to="Percent")

Click here to see how this interactive table was created

This table was created using the datatable() function of the DT package.

Like so:

DT::datatable(asian_subgroups)

Checking the data


OK, now let’s make sure that our notations match across our different tables. For example in the first report the terms male and female where used, but in the second report men and women were used. Let’s make sure everything is consistent now.

major_groups_long
# A tibble: 108 x 5
   Group         Race_Ethnicity Gender  Year Percent
   <chr>         <chr>          <chr>  <dbl>   <dbl>
 1 United States All_races      All     2008    12.6
 2 United States All_races      All     2010    14.7
 3 United States All_races      All     2012    14.1
 4 United States All_races      All     2014    13.2
 5 United States All_races      All     2016    11.7
 6 United States All_races      All     2017    11.5
 7 Male          All_races      Male    2008    12.3
 8 Male          All_races      Male    2010    15.2
 9 Male          All_races      Male    2012    14.5
10 Male          All_races      Male    2014    13.3
# ... with 98 more rows
asian_subgroups
# A tibble: 45 x 5
   Group          Percent Race_Ethnicity Gender  Year
   <chr>            <dbl> <chr>          <chr>  <dbl>
 1 United States     11.5 All_races      All     2017
 2 Male              11.8 All_races      Male    2017
 3 Female            11.1 All_races      Female  2017
 4 Asian              6.6 Asian          All     2017
 5 Asian Male         6.5 Asian          Male    2017
 6 Asian Female       6.7 Asian          Female  2017
 7 Chinese            4.3 Chinese        All     2017
 8 Chinese Male       4.7 Chinese        Male    2017
 9 Chinese Female     3.9 Chinese        Female  2017
10 Vietnamese         5.5 Vietnamese     All     2017
# ... with 35 more rows
latinx_subgroups
# A tibble: 32 x 5
   Group                   Percent Race_Ethnicity   Gender  Year
   <chr>                     <dbl> <chr>            <chr>  <dbl>
 1 Latinx                     13.2 Latinx           All     2017
 2 Latinx Male                12.4 Latinx           Male    2017
 3 Latinx Female              13.9 Latinx           Female  2017
 4 South American              8.4 South American   All     2017
 5 South American Male         9.1 South American   Male    2017
 6 South American Female       7.7 South American   Female  2017
 7 Central American           12   Central American All     2017
 8 Central American Male       9.3 Central American Male    2017
 9 Central American Female    15   Central American Female  2017
10 Mexican                    13.3 Mexican          All     2017
# ... with 22 more rows

Looks good! If you made it this far, you should pat yourself on your back. That was a lot of wrangling!

Now let’s save the wrangled data so we can use it for later if we wanted to start again at this point. We can do so using the save function like so. We will save this data in a subdirectory called “wrangled” within the “data” directory of our working directory. We will also save a csv file as this is often useful to send our data to collaborators. To use the write_csv() function we need to save a different CSV file for each tibble.

save(major_groups_long, asian_subgroups, latinx_subgroups, file = here::here("data", "wrangled", "wrangled_data.rda"))
readr::write_csv(major_groups_long, file = here::here("data", "wrangled", "wrangled_major_groups_data.csv"))
readr::write_csv(asian_subgroups, file = here::here("data", "wrangled", "wrangled_asian_subgroups_data.csv"))
readr::write_csv(latinx_subgroups, file = here::here("data", "wrangled", "wrangled_latinx_subgroups_data.csv"))

Data Visualization


Recall what our main questions were?

Our main questions:

  1. How have youth disconnection rates in American youth changed since 2008?
  2. In particular, how has this changed for different gender and ethnic groups? Are any groups particularly disconnected?

Now that we have wrangled our data and made it easy to work with, let’s create some visualizations to explore these questions.

If you have been following along but stopped and are starting here you could load the wrangled data using the following command:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data wrangling section click here.

First you need to install the OCSdata package:

install.packages("OCSdata")

Then, you may load the wrangled data into your R environment using the following code:

# library(OCSdata)
wrangled_rda("ocs-bp-youth-disconnection". outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

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 within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_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 and here to learn more about the here package.



Major Race and Ethnic Groups Plot


We are particularly interested in being able to reproduce the plot below from the report, as we would like to make similar looking plots.

In general, it is very useful to learn how to reproduce the style of a plot.

There are color identifying websites such as this.

Using one of these websites, we identify the hex triplet code for the color used in the visualization included in the PDF : #008393. Thus we will use this color our plot.

We can create a version of the above plot using the ggplot2 package of the tidyverse to create our plots.


Click here for an introduction about this package if you are new to using 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.


So for this first plot we just want to recreate the plot in the report. We will use the major_groups_long tibble and we will filter for only the "All" values of the Gender column. We also want to exclude the data for the United states. We can exclude the data from the US using the not equals != operator like so:

major_groups_long %>%
   filter(Gender == "All",
          Group != "United States")
# A tibble: 30 x 5
   Group Race_Ethnicity Gender  Year Percent
   <chr> <chr>          <chr>  <dbl>   <dbl>
 1 Asian Asian          All     2008     7.1
 2 Asian Asian          All     2010     8.5
 3 Asian Asian          All     2012     7.8
 4 Asian Asian          All     2014     7.9
 5 Asian Asian          All     2016     6.6
 6 Asian Asian          All     2017     6.6
 7 White White          All     2008     9.7
 8 White White          All     2010    11.7
 9 White White          All     2012    11.2
10 White White          All     2014    10.8
# ... with 20 more rows

The data from the tables only included up to 2017, but this will still allow us to create a similar plot.

Now we have the data ready to make the plot… but how do we actually start making the plot?

First, we start with the ggplot() function of 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. It also involves choosing variables to color or group our plot by.

In our case, we want to plot the percent of disconnection on the y-axis (thus the Percent variable of the major_groups_long data) and the Year on the x-axis. We want to separate each racial or ethnic group to have their own points/lines. Thus we will use the color argument for this variable, as we intend to color our plot something other than black.

If we run the following code, we get an empty plot.

major_groups_long %>%
   filter(Gender == "All",
          Group != "United States") %>%
ggplot(aes(x = Year, y = Percent, color = Race_Ethnicity))

The next thing we need to do is add ggplot2 layers using the + operator to specify how we want the data to be displayed on our plot.

We would like both points and lines. We will use the geom_point() and geom_line() functions of the ggplot2 package to do this. The layer we add first will be plotted first and so on with the next layers. We will also specify the size of these elements using the size argument.

major_groups_long %>%
  filter(Gender == "All", Group != "United States") %>%
  ggplot(aes(x = Year, y = Percent, color = Race_Ethnicity)) +
    geom_line(size = 0.5) +
    geom_point(size = 3) 

OK, not bad, but we have quite a bit to work on to make the plot style match.

First, we will update the x-axis and y-axis, to look more similar to the plot from the report. We can specify the location of the tick marks using breaks argument of the scale_x_continuous() and scale_y_continuous() functions (also of the ggplot2 package). These functions also allow 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 will make the x-axis upper limit a bit larger to allow for the image of the figure and the label for each group.

Next, we will change the color to match the one that we identified, by using the scale_color_manual() function of the ggplot2 package. This requires color values for each group. In our case, we have 5 groups, so we repeat this value 5 times using the base rep() function.

We will also change the overall look of the plot using the theme_classic() function. See here for a list of options.

 youth_plot <- 
  major_groups_long %>%
  filter(Gender == "All", Group != "United States") %>%
  ggplot(aes(x = Year, y = Percent, color = Race_Ethnicity)) +
    geom_line( size = 0.5) +
    geom_point( size = 3) +
    scale_x_continuous(breaks = seq(2008,2018, by=1),
                       limits = c(2008,2020)) +
    scale_y_continuous(breaks = seq(5,30, by =5),
                       limits = c(5,30)) +
    scale_color_manual(values = c(rep( "#008393", 5))) + 
    theme_classic()

youth_plot

Now let’s add some labels. We can add a title using the title argument and a y-axis title using the y argument of the labs() function.

youth_plot <- 
  youth_plot + 
  labs(title = "YOUTH DISCONNECTION BY RACE AND ETHNICITY, 2008 - 2017",
           y = "YOUTH DISCONNECTION (%)") +
  theme(title = element_text(size = 10,
                             color = "#008393",
                             face = "bold"),
        axis.title.x = element_blank())

youth_plot <- 
  directlabels::direct.label(youth_plot, 
                             list(dl.trans(x = x+1.2, y = y + 0), 
                                  "last.points"))
youth_plot

Getting close!

Now we need to add figurine icons to the plot.

To add the figurines we can use icons from free resources like font awesome. See here for a link to svg options. Or we could also take a screenshot of just the figurine from the original plot. However, it is useful to know how to create similar icons if we didn’t have the original plot to work with so we will start with an example of using a font awesome icon.

Using font awesome:

First we need to import the image from font awesome:

fa_figurine <- image_read("https://upload.wikimedia.org/wikipedia/commons/7/7c/User_font_awesome.svg")

fa_figurine

We would like to change the color of the icon to match that of our plot. We can do so using the image_fill() function of the magick package. We need to list the name of the image object to change, then we need to list the color we want to use. Finally we need to list what points we want to color. We don’t want to color the space around the black shapes, just the black shapes themselves. It may take some trial and error to figure out where these pixels are.

According to the documentation for the magick package:

The image_fill() function performs flood-fill by painting starting point and all neighboring pixels of approximately the same color.

point = a geometry_point string indicating the starting point of the flood-fill

First we will color in the circle at the top of the icon. If we use point = +600+600 this is starting at the pixel at x:600, y:600. We can see from the output about the fa_figurine that the image is 1792 by 1792 pixels in size. Thus, if we choose a pixel that is within the borders of the shape we want to change then we can change that shape. We can use the size of the image to figure out where the pixel might be. Since the image is 1792 by 1792 pixels, 800 is about half way through the image image from the left side (x:0) and 600 is about a third of the way through the and the top (y:0).

fa_figurine <- image_fill(fa_figurine,
                          color = "#008393", 
                          point = "+800+600")

fa_figurine

Second we will color in the lower shape of the icon. This shape is in the lower half of the image and in the middle of the image according to the x axis, thus we will start at the pixel that is at about half way for the width at x:800 and a bit more than half way down from the top (y:0) for the height y:1000.

fa_figurine <- image_fill(fa_figurine,
                          color = "#008393", 
                          point = "+800+1000")

fa_figurine

Nice! We changed the color of the image.

Now let’s try using a screenshot of the original plot. You can find our screenshot in our img directory on our GitHub repository.

Using a screenshot:

figurine <-image_read(here::here("img", "figurine.png"))
figurine

Now to add the image of the figurine to the plot we can use the draw_image() function of the cowplot package. We simply need to specify the name of the image, and then where we would like it to go according to the x- and y-axis and finally the scaled size of the image. This scale argument also takes a bit of trial and error.

Here we will add both images of the figurines to a couple of groups:

youth_plot +
  cowplot::draw_image(figurine, x = 2017, y = 23.5, scale = 4) +
  cowplot::draw_image(fa_figurine, x = 2017, y = 17.5, scale = 2)

Nice!

Now we just need to add this for all of the groups and we also will want to replace our Native American label with a two line version so that we look as similar to the report plot as possible.

Question Opportunity

How might we change the Native American label to have two lines? (hint: think about how we separated the rows of our tables)


Click here to reveal the code.
youth_plot <- major_groups_long %>%
   filter(Gender == "All",
          Group != "United States") %>%
   mutate(Race_Ethnicity = str_replace(string = Race_Ethnicity, 
                                      pattern = "Native American",
                                  replacement = "Native\nAmerican"))  %>% 
  ggplot(aes(x = Year, y = Percent, color = Race_Ethnicity)) +
    geom_line( size = 0.5) +
    geom_point( size = 3) +
    scale_x_continuous(breaks = seq(2008,2018, by=1),
                      limits = c(2008,2019)) +
    scale_y_continuous(breaks = seq(5,30, by =5),
                      limits = c(5,30)) +
    theme_classic() +
    labs(title = "YOUTH DISCONNECTION BY RACE AND ETHNICITY, 2008 - 2017",
         y = "YOUTH DISCONNECTION (%)") +
    theme(title = element_text(size = 10,
                              color = "#008393",
                              face = "bold"),
          axis.title.x = element_blank())+
    scale_color_manual(values = c(rep( "#008393", 5)))+
    draw_image(figurine, x = 2017.25, y = 23.5, scale = 4) +
    draw_image(figurine, x = 2017.25, y = 17.5, scale = 4) +
    draw_image(figurine, x = 2017.25, y = 13.5, scale = 4) +
    draw_image(figurine, x = 2017.25, y = 9, scale = 4) +
    draw_image(figurine, x = 2017.25, y = 5.5, scale = 4)

youth_plot <- directlabels::direct.label(youth_plot, list(dl.trans(x = x + 1.4, 
                                                                   y = y + 0),  
                                                               "last.points"))

youth_plot

Nice! Our plot looks very similar stylistically to the plot in the report.

We can see from this plot that Native Americans have a very high rate of youth disconnection, with roughly still a quarter of the population experiencing youth disconnection in this survey. Although some groups show a downward trend, like the Latinx group, the level has been fairly stable since 2008, as we see that the slope of each line is fairly flat. We will investigate this further in our analysis.

Major Race and Ethnic Groups and Gender Plot


Now let’s create a similar plot for females and males. To do this we can simply use the facet_wrap() function. We need to specify what variable we want to facet by, in this case the Gender variable, by using the ~ operator. We also want to set the scales argument to "free", so that we have a y-axis for both the female and male plots. See this case study for more information about faceting plots.

We do not want to plot the "All" values for the Gender variable as we have already shown that in the previous plot, so we again exclude it.

Question Opportunity

How do we modify the plot above to not include the "All" values for the Gender variable and stratify by Gender?


Click here to reveal the code.
plot_by_gender <- major_groups_long %>%
   filter(Race_Ethnicity != "All_races", Gender != "All") %>%
   mutate(Race_Ethnicity = str_replace(string = Race_Ethnicity, 
                                      pattern = "Native American",
                                  replacement = "Native\nAmerican"),
                  Gender = str_replace(string = Gender, 
                                      pattern = "Female",
                                  replacement = "FEMALE"),
                  Gender = str_replace(string = Gender, 
                                      pattern = "Male",
                                  replacement = "MALE")) %>%
  ggplot(aes(x = Year, y = Percent, color = Race_Ethnicity)) +
    geom_line(size = 0.5) +
    geom_point(size = 3) +
    facet_wrap(~Gender, scales = "free") +
    scale_x_continuous(breaks = seq(2008,2018, by=1),
                       limits = c(2008,2020)) +
    scale_y_continuous(breaks = seq(5,30, by =5),
                       limits = c(5,31)) +
    theme_classic() +
    labs(title = "YOUTH DISCONNECTION BY GENDER AND RACIAL/ETHNIC GROUP",
         y = "YOUTH DISCONNECTION (%)") +
    theme(title = element_text(size = 10,
                               color = "#008393",
                               face = "bold"),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90),
          strip.background =element_rect(fill="#003661"),
          strip.text = element_text(colour = 'white', face = "bold", 
                                    size = 12)) +
  scale_color_manual(values = c(rep( "#008393", 6)))
 
plot_by_gender <- direct.label(plot_by_gender, list(dl.trans(x = x+0.3, y = y +0), "last.points"))

plot_by_gender

It’s a bit difficult to tell the Black female and Latinx female lines apart.

Let’s try adding another color for every other line. Again we will use the scale_color_manual() function to specify the colors of the lines and points on the plot.

We will use black as the other color. We can use the hex triplet code or we can write out the name of the color, in this case we will write "black". To specifically color each group differently, we can write the colors in the alphabetically order of the distinct values of Race_Ethnicity. Thus the first color listed will be the color for the Asian values, while the last will be the color for the White values.

plot_by_gender <- 
  plot_by_gender +
  scale_color_manual(values = c("#008393","black", 
                                "#008393", "#008393",
                                "black"))

Question Opportunity

Can you explain why we didn’t use a pipe in the previous code chunk?

plot_by_gender

Very interesting! By parsing the groups further into intersections of racial and ethnic groups with gender, we see that there are some very striking differences for the Black and Latinx groups when stratified by Gender. One positive thing that we can see, is that there has been a steep decline in youth disconnection for Latinx females. Unfortunately, the level of disconnection for females in 2008 was quite high, so now Latinx females have similar rates to Latinx males. In contrast, we see that Black males have had and continue to have much higher rates of disconnection than Black females. Both Black females and males show an increased rate since 2016. Native American females also show an increased rate since 2016.

If you have time, the next section will show how to make bar plots to look for differences in subgroups for the Asian and Latinx data. Otherwise, you can skip ahead to the Data Analysis section.

Subgroup plots


It is clear from the previous plot that observing more specific subgroups in our data can be very informative!

Thus, we also want to make plots of the Asian and Latinx subgroups, to see if there are particular ethnic groups that have higher levels of youth disconnection. If so, these groups may particularly benefit for prevention and re-engagement efforts.

We will also attempt to continue to plot the genders separately, as we have learned that there may be important gender group differences among the racial and ethnic groups. However, the data is incomplete for some of the ethnic groups. Recall that we also only have two years of data for both our Asian and Latinx subgroup data. First we will start by plotting just the subgroups over the two years.

We can continue to make our plots look like they match the report by using a color palette based off the color used in the report. We can use the colorRampPalette() function of the grDevices package, which is loaded automatically in an RStudio session. We can specify that we want the colors to range from gray to the color that we have been using.

Then we just need to specify how many colors we want in our gradient when we choose to use the color palette. Say we wanted 4 colors, we just need to type custom_pal(4) to get a list of 4 colors within the palette. Thus we will have gray, the teal color we have been using, and two shades in between. After doing this we chose to use one of the colors in between and to use just two colors for our palette.

custom_pal <- colorRampPalette(c("gray", "#008393"))
custom_pal <- custom_pal(4)
custom_pal
[1] "#BEBEBE" "#7EAAAF" "#3F96A1" "#008393"
custom_pal <- c("#008393","#BEBEBE")

Here we can see the hex triplet codes for gray and the shade in between.

OK, now let’s start with the Asian subgroups. Since we only have two time points of data for each group, we will create a bar graph this time where the two years are displayed next to each other for each year. Thus, we want the Year variable to be a discrete variable. To ensure that it is interpreted as a discrete variable, we need to convert it to be a character type variable rather than numeric using the base as.character() function.

First, we want to filter for the rows where the Gender variable values are All, we also do not want to include the data for the average of all races.

asian_subgroups %>%
  filter(Gender == "All") %>%
  filter(Race_Ethnicity != "All_races") %>%
  mutate(Year = as.character(Year))
# A tibble: 17 x 5
   Group       Percent Race_Ethnicity Gender Year 
   <chr>         <dbl> <chr>          <chr>  <chr>
 1 Asian           6.6 Asian          All    2017 
 2 Chinese         4.3 Chinese        All    2017 
 3 Vietnamese      5.5 Vietnamese     All    2017 
 4 Indian          5.9 Indian         All    2017 
 5 Pakistani       6.4 Pakistani      All    2017 
 6 Korean          6.5 Korean         All    2017 
 7 Two Or More     6.6 Two Or More    All    2017 
 8 Filipino        7.3 Filipino       All    2017 
 9 Hmong          14   Hmong          All    2017 
10 Chinese         4.1 Chinese        All    2018 
11 Indian          5.4 Indian         All    2018 
12 Korean          5.5 Korean         All    2018 
13 Vietnamese      6.3 Vietnamese     All    2018 
14 Filipino        6.8 Filipino       All    2018 
15 Hmong          10.2 Hmong          All    2018 
16 Cambodian      13.8 Cambodian      All    2018 
17 Asian           6.2 Asian          All    2018 

Now that we have wrangled the data we can make our plot. To make a bar plot there are two main ggplot2 functions, geom_col() and geom_bar(). The geom_col() function plots the actual values of the data, while the geom_bar()function plots counts (however you can override this with the stat = identity argument). We are interested in plotting the actual values, so we will use the goem_col() function.

This time we will specify that the Year variable be used to specify the fill color of the bars of the bar plot by using aes(fill = Year).

We also need to indicate how the bars should be plotted based on the position argument.

The options for the position argument are:

  • stack - the years would be displayed on top of one another (this is default)
  • dodge - the years would be displayed next to one another with no space in between
  • dodge2 - the years would be displayed next to one another with a space in between
  • fill - the years would be displayed on top of one other, where the heights of each color would show the relative proportions for each year adding up to 1, thus each bar would have the same height

We will use the dodge option.

For the colors, we can try out various numbers of shades for the palette until we get shades that we like. In this case, 2 shades out of a gradient of 4 shades from gray to teal looks nice,

asian_subgroups %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  mutate(Year = as.character(Year)) %>%
  ggplot(aes(x = Race_Ethnicity, y = Percent)) +
    geom_col(aes(fill = Year), position = "dodge")+
    labs(title = "YOUTH DISCONNECTION BY ASIAN SUBGROUP, 2017-2018",
         y = "YOUTH DISCONNECTION (%)") +
    scale_fill_manual(values = custom_pal) +
    theme_classic() +
    theme(title = element_text(size = 10, color = "#008393",
                               face = "bold"),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90, hjust = 1))

OK, this looks pretty nice! However, we can improve this a bit.

Before we do so, let’s create a theme for our future similar plots like so:

bar_theme <- function() {
  theme_classic() +
  theme(title = element_text(size = 10,
                             color = "#008393",
                             face = "bold"),
        axis.title.x = element_blank(),
        axis.text.x = element_text(angle = 90, hjust = 1))
  }

Now we simply need to type bar_theme() instead to achieve the same style for our plot.

It would be nice if for subgroups that only have one year of data, if the column that was displayed was still the same width as the that of the other groups. To do this we need a row with an NA value for the year that is missing. One way to accomplish this is to use the pivot_wider() and pivot_longer() functions to widen the data based on the Year variable and the collapse the data based on the Year variable. By widening our data, we create the NA values we want; however we need to collapse our data back to the long format so that we can easily use ggplot2 to plot the year as the fill in our bar plot.

Note that we no longer need to change the type of the Year variable, as it is automatically converted to type character.

asian_subgroups %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  dplyr::select(-Group) %>%
  pivot_wider(names_from = Year, values_from = Percent)
# A tibble: 10 x 4
   Race_Ethnicity Gender `2017` `2018`
   <chr>          <chr>   <dbl>  <dbl>
 1 Asian          All       6.6    6.2
 2 Chinese        All       4.3    4.1
 3 Vietnamese     All       5.5    6.3
 4 Indian         All       5.9    5.4
 5 Pakistani      All       6.4   NA  
 6 Korean         All       6.5    5.5
 7 Two Or More    All       6.6   NA  
 8 Filipino       All       7.3    6.8
 9 Hmong          All      14     10.2
10 Cambodian      All      NA     13.8

Great! Now we have NA values. Now we just to get the data back into long format:

asian_subgroups %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  dplyr::select(-Group) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to ="Year" , 
               values_to="Percent")
# A tibble: 20 x 4
   Race_Ethnicity Gender Year  Percent
   <chr>          <chr>  <chr>   <dbl>
 1 Asian          All    2017      6.6
 2 Asian          All    2018      6.2
 3 Chinese        All    2017      4.3
 4 Chinese        All    2018      4.1
 5 Vietnamese     All    2017      5.5
 6 Vietnamese     All    2018      6.3
 7 Indian         All    2017      5.9
 8 Indian         All    2018      5.4
 9 Pakistani      All    2017      6.4
10 Pakistani      All    2018     NA  
11 Korean         All    2017      6.5
12 Korean         All    2018      5.5
13 Two Or More    All    2017      6.6
14 Two Or More    All    2018     NA  
15 Filipino       All    2017      7.3
16 Filipino       All    2018      6.8
17 Hmong          All    2017     14  
18 Hmong          All    2018     10.2
19 Cambodian      All    2017     NA  
20 Cambodian      All    2018     13.8

Great, now let’s see how this looks in the plot.

asian_subgroups %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  dplyr::select(-Group) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to ="Year" , 
               values_to="Percent") %>%
  ggplot(aes(x = Race_Ethnicity, y = Percent)) +
    geom_col(aes(fill = Year), position = "dodge")+
    labs(title = "YOUTH DISCONNECTION BY ASIAN SUBGROUP, 2017-2018",
         y = "YOUTH DISCONNECTION (%)") +
    scale_fill_manual(values = custom_pal) +
    bar_theme()

Great! that is looking better! However, currently the subgroups are plotted on the x-axis by alphabetically order and we want to instead order the subgroups based on the percentage of youth disconnection. We can use the forcats package to do this.

The fct_reorder() function can be used to order the Race_Ethnicity variable based on the average of the Percent variable for the two years, while the fct_relevel() function can be used to make the Asian level appear first for comparison sake. Importantly, we need to do this before we reshape the data, because subgroups with NA values will be placed at the end.

asian_subgroups %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity = fct_reorder(Race_Ethnicity, Percent), 
         Race_Ethnicity = fct_relevel(Race_Ethnicity, "Asian")) %>% 
  select(-Group) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to ="Year", values_to="Percent")%>%
  ggplot(aes(x = Race_Ethnicity, y = Percent)) +
    geom_col(aes(fill = Year), position = "dodge")+
    labs(title = "YOUTH DISCONNECTION BY ASIAN SUBGROUP",
         subtitle = "ORDERED BY AVERAGE DISCONNECTION LEVEL OF 2017 & 2018",
         y = "YOUTH DISCONNECTION (%)") +
    scale_fill_manual(values = custom_pal) +
    bar_theme()

This is looking very good! Now, let’s make a gap between the Asian average group and the subgroups to more easily differentiate between the two and let’s use that space to add the figure legend to the plot itself.

To first make a gap, we can use the scale_x_discrete() function. We need to write the name of the levels of the Race_Ethnicity variable and place a blank in between the Asian level and the subsequent levels. We can use the levels() function to make this more reproducible and to avoid mistakes instead of actually writing out each of the levels by hand.

Here you can see all the levels:

asian_subgroups %>%
  dplyr::select(-Group) %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity =  fct_reorder(Race_Ethnicity, Percent),
         Race_Ethnicity = fct_relevel(Race_Ethnicity, "Asian")) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to = "Year", values_to = "Percent")%>%
  pull(Race_Ethnicity) %>%
  levels()
 [1] "Asian"       "Chinese"     "Indian"      "Vietnamese"  "Korean"     
 [6] "Pakistani"   "Two Or More" "Filipino"    "Hmong"       "Cambodian"  

To simplify our code we will save our wrangled data as a tibble called df_asian_subgroup.

So we would like our x-axis to be like this:

scale_x_discrete(
  limits = c("Asian",
             "Blank",
             "NEXT_blank",
             levels(pull(df_asian_subgroup, Race_Ethnicity))[2:10]),
  labels = c("Blank" = "",
             "NEXT_blank"= ""))

The levels(pull(df_asian_subgroup, Race_Ethnicity))[2:10]) code gets all 9 other levels but the Asian level of the Race_Ethnicity variable. The labels argument specifies what the x-axis should say for the spaces in between. We would like them to say nothing, so we use nothing within quotes "" as the label.

We can also use this as an opportunity to change the label on the plot for the Asian level of the Race_Ethnicity variable to be "Asian avg.". We don’t want to change the other subgroup level labels so we will not list anything.

We can make this more reproducible by using the length() function instead of the number 10 and replacing Asian with the first level:

scale_x_discrete(
  limits = c(levels(pull(df_asian_subgroup, Race_Ethnicity))[1],
             "Blank",
             "NEXT_blank",
             levels(pull(df_asian_subgroup, Race_Ethnicity))[2:
             length(levels(pull(df_asian_subgroup, Race_Ethnicity)))]),
  labels = c("Asian" = "Asian Avg.",
             "Blank" = "",
             "NEXT_blank"= ""))

Then we can use legend.justification and legend.position of the theme() function to move our legend to the plot area.

According to the documentation for the theme() function of the ggplot2 package, the legend.justification argument specifies the:

anchor point for positioning legend inside plot (“center” or two-element numeric vector) or the justification according to the plot area when positioned outside the plot

By setting the mapping for the positioning and justification as c(0.12,0.1) we specify that we want the legend to be 12 percent of the plot area from the y-axis and 10 percent of the plot area from the x-axis.

df_asian_subgroup <-
  asian_subgroups %>%
  dplyr::select(-Group) %>%
  filter(Gender == "All", 
         Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity =  fct_reorder(Race_Ethnicity, Percent), 
         Race_Ethnicity = fct_relevel(Race_Ethnicity, "Asian")) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to = "Year", values_to = "Percent")

asian_subgroup_plot <-
  df_asian_subgroup %>%
  ggplot(aes(x = Race_Ethnicity, y = Percent, fill = Year)) +
    geom_col(aes(fill = Year), position = "dodge")+
    labs(title = "YOUTH DISCONNECTION BY ASIAN SUBGROUP",
         subtitle = "ORDERED BY AVERAGE DISCONNECTION LEVEL OF 2017 & 2018",
         y = "YOUTH DISCONNECTION (%)") +
  scale_fill_manual(values = custom_pal) +
  scale_x_discrete(
    limits = c(levels(pull(df_asian_subgroup,Race_Ethnicity))[1],
             "Blank",
             "NEXT_blank",
             levels(pull(df_asian_subgroup,Race_Ethnicity))[2:
             length(levels(pull(df_asian_subgroup,Race_Ethnicity)))]),
    labels = c("Asian" = "Asian Avg.", "Blank" = "", "NEXT_blank"= "")) +
  scale_y_continuous(limits = c(0,max(pull(asian_subgroups,Percent), 
                                       na.rm = TRUE)))+
  bar_theme() +
  # this add the legend to the plot area!:
        theme(legend.justification = c(0.12,0.1),
              legend.position = c(0.12,.1))

asian_subgroup_plot

Nice! But, this is still missing something. Perhaps if we add lines that show the average disconnection rate for the US and for Asians in general, then it will make it easier to see differences in our plot.

In the previous code we saved our plot to a object called asian_subgroup_plot.

We can modify it by adding layers to add the lines that we want.

First, we want to create tibbles for the US Asian average disconnection rates. We want to add labels to each of these. We can use the parse = TRUE argument later with the geom_text() function of the ggplot2 package to make these labels appear as bold font as it will be evaluated as an expression instead of a simple character string.

df_asian_total <- 
  asian_subgroups %>%
  filter(Year == 2017, Gender == "All", Race_Ethnicity == "Asian") %>%
  mutate(Year = as.character(Year), 
         label = c('bold("ASIAN 2017 AVG RATE")'))

df_US_total <- 
  asian_subgroups %>%
  filter(Year == 2017, Gender == "All", Race_Ethnicity == "All_races") %>%
  mutate(Year = as.character(Year), label = c('bold("US 2017 AVG RATE")'))

df_asian_total
# A tibble: 1 x 6
  Group Percent Race_Ethnicity Gender Year  label                          
  <chr>   <dbl> <chr>          <chr>  <chr> <chr>                          
1 Asian     6.6 Asian          All    2017  "bold(\"ASIAN 2017 AVG RATE\")"
df_US_total
# A tibble: 1 x 6
  Group         Percent Race_Ethnicity Gender Year  label                       
  <chr>           <dbl> <chr>          <chr>  <chr> <chr>                       
1 United States    11.5 All_races      All    2017  "bold(\"US 2017 AVG RATE\")"

Now we can use these to create labels again with the geom_text() function and lines with the geom_hline() function on our plot.

The geom_text() function requires that the x and y axis location for the text be specified, and we want this to be located near the actual percent disconnection rate, thus we use the Percent +.5 to grab the Percent value from df_asian_total and add 0.5 to it to be slightly above where the line will be.

The geom_hline() function requires, just the y intercept as this function always creates a horizontal line. The linetype argument specifies what style of line we would like. See here for a list of options.

asian_subgroup_plot <- 
  asian_subgroup_plot + 
  geom_text(data = df_asian_total, 
            mapping = aes(x = 2.5, y = Percent +.7, label = label),
            parse = TRUE,
            color = "#008393", 
            size = 4) +
  geom_hline(aes(yintercept = pull(df_asian_total, Percent)), linetype = 2) +
  geom_text(data = df_US_total,
            mapping = aes(x = 2.5, y = Percent +.7, label = label),
            parse = TRUE,
            color = "#008393", 
            size = 4) +
  geom_hline(aes(yintercept = pull(df_US_total, Percent)), linetype = 3)

asian_subgroup_plot

Awesome! Now it is much easier to tell how the disconnection rates for various subgroups compare to the national average and the Asian average. From our plot, we can see that the Hmong and Cambodian subgroup rates are very high. If we only had the plot of major racial and ethnic groups to rely on, we might not realize that these two groups have such high rates.

Nice! This shows the importance of adding small details such as the US and Asian rate lines. This helps provide a simple yet nuanced picture of what is going on.

From the above plot, it becomes readily apparent that the Hmong and Cambodian subgroups have much higher disconnection rates than the other Asian subgroups, as well as all races / ethnicities in the US combined.

If we wanted to make a similar plot but without the subgroups that are missing a year we could do it like so, by adding the drop_na() function of the tidyr package to remove rows with NA values when the data is in wide form and then using the fct_drop() function of the forcats package to remove the levels of the Race_Ethnicity variable that no longer have rows in the data.

Click here to reveal the code.
df_asian_subgroup <-
  asian_subgroups %>%
  dplyr::select(-Group) %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity =  fct_reorder(Race_Ethnicity, Percent), 
         Race_Ethnicity = fct_relevel(Race_Ethnicity, "Asian")) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  tidyr::drop_na() %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
               names_to = "Year", values_to = "Percent") %>%
  mutate(Race_Ethnicity = fct_drop(Race_Ethnicity))

asian_subgroup_plot_simp <-
  df_asian_subgroup %>%
  ggplot(aes(x = Race_Ethnicity, y = Percent, fill = Year)) +
    geom_col(aes(fill = Year), position = "dodge")+
    labs(title = "YOUTH DISCONNECTION BY ASIAN SUBGROUP",
         subtitle = "ORDERED BY AVERAGE DISCONNECTION LEVEL OF 2017 & 2018",
         y = "YOUTH DISCONNECTION (%)") +
    scale_fill_manual(values = custom_pal) +
    scale_x_discrete(
    limits = c(levels(pull(df_asian_subgroup,Race_Ethnicity))[1],
               "Blank",
               "NEXT_blank",
               levels(pull(df_asian_subgroup,Race_Ethnicity))[2:
               length(levels(pull(df_asian_subgroup,Race_Ethnicity)))]),
    labels = c("Asian" = "Asian Avg.",
               "Blank" = "",
               "NEXT_blank"= ""))+
    scale_y_continuous(limits = c(0,max(pull(asian_subgroups,Percent), 
                                        na.rm = TRUE)))+
    bar_theme() +
    #this add the legend to the plot area!:
    theme(legend.justification = c(0.16,0.01),legend.position = c(0.16,.01)) +
    geom_text(data = df_asian_total,
              mapping = aes(x = 2.5, y = Percent +.7, label = label),
              parse = TRUE, color = "#008393", size = 4) +
    geom_hline(aes(yintercept = pull(df_asian_total, Percent)), linetype = 2) +
    geom_text(data = df_US_total,
              mapping = aes(x = 2.5, y = Percent +.7, label = label),
              parse = TRUE, color = "#008393", size = 4) +
    geom_hline(aes(yintercept = pull(df_US_total, Percent)), linetype = 3)
asian_subgroup_plot_simp


Now let’s do the same for the Latinx subgroups.

First let’s make the small tibble of the 2017 Latinx average rate like we did for the US and for Asians to create the lines and text on our plot.

df_latinx_total <- 
  latinx_subgroups %>%
  filter(Year == 2017, Gender == "All", Race_Ethnicity == "Latinx") %>%
  mutate(Year = as.character(Year), 
         label = c('bold("LATINX 2017 AVG RATE")'))

Now we will use similar code for the latinx subgroups. The only difference is that we will also change the "Puerto Rican, Dominican, Cuban" values to have new line breaks between the groups. First let’s plot all subgroups.

Click here to reveal the code.
latinx_for_plot <-latinx_subgroups %>%
  dplyr::select(-Group) %>%
  filter(Gender == "All", Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity = str_replace(
                 string = Race_Ethnicity, 
                pattern = "Puerto Rican, Dominican, Cuban",
            replacement = "Puerto Rican,\nDominican,\nCuban")) %>%
  mutate(Race_Ethnicity = fct_reorder(Race_Ethnicity, Percent), 
         Race_Ethnicity = fct_relevel(Race_Ethnicity, "Latinx")) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
  names_to = "Year" , 
  values_to = "Percent")

latinx_subgroup_plot <- 
  latinx_for_plot %>%
  ggplot(aes(x = Race_Ethnicity, y = Percent, fill = Year)) +
    geom_col(aes(fill = Year), position = "dodge") +
    labs(title = "YOUTH DISCONNECTION BY LATINX SUBGROUP",
         subtitle = "ORDERED BY AVERAGE DISCONNECTION LEVEL OF 2017 & 2018",
         y = "YOUTH DISCONNECTION (%)") +
    scale_fill_manual(values = custom_pal) +
    scale_x_discrete(
    limits = c(levels(pull(latinx_for_plot,Race_Ethnicity))[1],
               "Blank",
               "NEXT_blank",
               levels(pull(latinx_for_plot,Race_Ethnicity))[2:
               length(levels(pull(latinx_for_plot,Race_Ethnicity)))]),
    labels = c("Latinx" = "Latinx Avg.",
               "Blank" = "",
               "NEXT_blank"= ""))+
    bar_theme() +
    #this add the legend to the plot area!:
    theme(legend.justification = c(0.23,0.1),legend.position = c(0.23,.1))+
    geom_text(data = df_latinx_total,
           mapping = aes(x = 3, y = Percent +.7, label = label),
             parse = TRUE, color = "#008393", size = 4) +
    geom_hline(aes(yintercept = pull(df_latinx_total, Percent)), linetype = 2) +
    geom_text(data = df_US_total,
           mapping = aes(x = 3, y = Percent +.7, label = label),
             parse = TRUE, color = "#008393", size = 4) +
    geom_hline(aes(yintercept = pull(df_US_total, Percent)), linetype = 3)
latinx_subgroup_plot

Now let’s drop the subgroups that don’t have values for both years.

Click here to reveal the code.
latinx_for_plot <-latinx_subgroups %>%
  dplyr::select(-Group) %>%
  filter(Gender == "All") %>%
  filter(Race_Ethnicity != "All_races") %>%
  mutate(Race_Ethnicity = str_replace(
                 string = Race_Ethnicity, 
                pattern = "Puerto Rican, Dominican, Cuban",
            replacement = "Puerto Rican,\nDominican,\nCuban")) %>%
  mutate(Race_Ethnicity =  fct_reorder(Race_Ethnicity, Percent)) %>% 
  mutate(Race_Ethnicity = fct_relevel(Race_Ethnicity, "Latinx")) %>%
  pivot_wider(names_from = Year, values_from = Percent) %>%
  tidyr::drop_na() %>%
  pivot_longer(cols = -c(Race_Ethnicity, Gender), 
  names_to = "Year" , 
  values_to = "Percent")%>%
  mutate(Race_Ethnicity = fct_drop(Race_Ethnicity))

latinx_subgroup_plot_simp <-latinx_for_plot %>%
ggplot(aes(x = Race_Ethnicity, y = Percent, fill = Year)) +
 geom_col(aes(fill = Year), position = "dodge")+
  labs(title = "YOUTH DISCONNECTION BY LATINX SUBGROUP",
    subtitle = "ORDERED BY AVERAGE DISCONNECTION LEVEL OF 2017 & 2018",
           y = "YOUTH DISCONNECTION (%)") +
  scale_fill_manual(values = custom_pal) +
  scale_x_discrete(
  limits = c(levels(pull(latinx_for_plot,Race_Ethnicity))[1],
             "Blank",
             "NEXT_blank",
             levels(pull(latinx_for_plot,Race_Ethnicity))[2:
             length(levels(pull(latinx_for_plot,Race_Ethnicity)))]),
  labels = c("Latinx" = "Latinx Avg.",
             "Blank" = "",
             "NEXT_blank"= ""))+
  scale_y_continuous(limits = c(0,max(pull(latinx_subgroups,Percent), 
                                       na.rm = TRUE)))+
  bar_theme()+
  #this add the legend to the plot area!:
  theme(legend.justification = c(0.23,0.01),legend.position = c(0.23,.01))+
  geom_text(data = df_latinx_total,
         mapping = aes(x = 3, y = Percent +.7, label = label),
           parse = TRUE,
           color = "#008393", 
            size = 4) +
  geom_hline(aes(yintercept = pull(df_latinx_total, Percent)), linetype = 2) +
  geom_text(data = df_US_total,
         mapping = aes(x = 3, y = Percent +.7, label = label),
           parse = TRUE,
           color = "#008393", 
            size = 4) +
  geom_hline(aes(yintercept = pull(df_US_total, Percent)), linetype = 3)
latinx_subgroup_plot_simp

Data Analysis


If you have been following along but stopped and are starting here you could load the wrangled data using the following command:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data wrangling section click here.

First you need to install the OCSdata package:

install.packages("OCSdata")

Then, you may load the wrangled data into your R environment using the following code:

# library(OCSdata)
wrangled_rda("ocs-bp-youth-disconnection", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

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 within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_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 and here to learn more about the here package.



In our data, we have pooled (repeated) cross-sectional data.

This is data produced from repeated measurement of a population over time.

It is often unfeasible to collect data for an entire population at once. However, we can still obtain meaningful measures using a random sample of the population.

At specific time-points, data is collected from a sample of the population. The individuals in each sample are not necessarily the same individuals. This separates pooled cross-sectional data from panel data, which is longitudinal data from repeated measurement of the same people.

By sampling from a population at multiple time points, we can generate estimates of population level statistics. Although these statistics have some random error, they can provide insight into how the measure variable is changing in a population over time.

As we have measured values over time, we can plot the data to see if there is a trend (increasing, decreasing, monotonic, non-monotonic, linear, non-linear, etc). Sometimes, however, the trend isn’t exactly clear. Fortunately, there are statistical methods to resolve this issue.

Next, we will introduce and discuss the Mann-Kendall trend test.

Mann-Kendall trend test


The Mann-Kendall trend test, (which is a variation of the Kendall rank correlation coefficient), tests whether there is a monotonic association between two variables, or a relationship with consistent direction between two variables, where one variable (typically time) determines the order of the other variable. Thus this test is most often used to see if there is a consistent change in direction (or mathematical sign) for a variable across time.

See this link for information on using this test with missing data.

Monotonic relationships


As you can see in the image below, a monotonic relationship does not need to be linear, it simply needs to show a consistent direction of change. A linear relationship on the other hand shows consistent direction and rate of change.

[source - Stephanie Glen. “Monotonic Relationship: Definition” From StatisticsHowTo.com: Elementary Statistics for the rest of us! https://www.statisticshowto.com/monotonic-relationship/]

So in other words, we are interested to see if there is a consistent direction in the relationship of disconnection rates with time, as we want to know if rates have been consistently increasing or decreasing or if they have been staying roughly the same.

Note: this test does not test if the rate of change has been consistent over time, just the direction.

Mann-Kendall trend test vs simple linear regression


In contrast to the Mann-Kendall trend test, we briefly compare this to Simple linear regression.

There are a couple of difference between the two:

  1. Linear regression assesses if there a consistent change in both rate and direction in the dependent variable as a single independent variable changes. The coefficients tell us how much the dependent variable changes with one unit change of the independent variable.

  2. The Mann-Kendall trend test is a nonparametric test, which means that it does not require as many assumptions as some parametric tests, like the simple linear regression.

M-K test by hand


The null hypothesis \(H_{0}\) of the test is that there is no monotonic trend, while the alternative hypothesis \(H_{a}\) is that there is a monotonic trend.

According to this report, the Mann-Kendall \(S\) score is calculated as follows:

The data values are evaluated as an ordered time series. Each data value is compared to all subsequent data values. The initial value of the Mann-Kendall statistic, \(S\), is assumed to be 0 (e.g., no trend). If a data value from a later time period is higher than a data value from an earlier time period, \(S\) is incremented by 1. On the other hand, if the data value from a later time period is lower than a data value sampled earlier, \(S\) is decremented by 1. The net result of all such increments and decrements yields the final value of \(S\).

\[S =\sum_{k=1}^{n-1}\sum_{j = k+1}^{n} sign(x_{j} - x_{k}) \]

where we assume there are \(n\) time points and observations \(x_{j}\) and \(x_{k}\) at two time points and:

\[sign(x_{j}-x_{k}) = 1 \text{, if } x_{j}-x_{k}>0\] \[sign(x_{j}-x_{k}) = 0 \text{, if } x_{j}-x_{k}=0\] \[sign(x_{j}-x_{k}) = -1 \text{, if } x_{j}-x_{k}<0\]

Another way of stating this is all possible cases where a value occurred later in time must be compared with values that occurred earlier in time, giving \(n(n–1)/2\) comparisons, like so if there were four time points (\(n = 4\)):

\[S = sign(x_2−x_1) + sign(x_3−x_1) + sign(x_4-x_1) + sign(x_3−x_2) + sign(x_4-x_2) + sign(x_4-x_3)\]

which is consistent with \(4(4-1)/2 = 4(3)/2 = 12/2 = 6\) comparisons.

Once we have calculated the Mann-Kendall \(S\) score, we want to know if the score is meaningfully large or small enough from what we would expect with data with no trend. To determine that, we need to know what the variance is of the Mann-Kendall \(S\) score (or how much variability we expect).

We won’t explain the details here, but it turns out the variance of the Mann-Kendall \(S\) score (or \(\text{var}(S)\)) is calculated like so:

\[ \text{var}(S)=\frac{1}{18}(n(n−1)(2n+5)−\sum_{p-1}^{g}t_{p}(t_{p}−1)(2t_{p}+5))\] where:

\[g = \text{number of tied groups}\] \[t_{p} = \text{number of observations in the }p^{th}\text{ group for a given tie}\]

For example, if 3 observations in our example were 35, then there would be 1 \(g\) tied group and 3 \(t_{p}\) observations.

Note: according to this source, a minimum of 4 observations should be used for this test and it is recommended to have 8 or more.

Putting this all together, the test statistic we are interested in (to know if the score is meaningfully large or small enough from what we would expect with data with no trend) \(Z_{MK}\) is calculated as follows:

if \(S > 0\) then \(Z_{MK} = \frac{S-1}{\sqrt{\text{var}(S)}}\)
if \(S < 0\) then \(Z_{MK} = \frac{S+1}{\sqrt{\text{var}(S)}}\)
if \(S = 0\) then \(Z_{MK} = 0\)

This test statistic \(Z_{MK}\) is considered a Z score (also known as a standard or standardized score). We can use standard look up tables from there to calculate a p-value. We will walk through an example of how to do this below.

Let’s conduct a Mann-Kendall trend test using our data to see if there are any monotonic trends in youth disconnection across time.

M-K trend test for Native Americans


Recall that the youth disconnection rates for Native Americans were some of the highest in the first table we examined.

and the plot we made:

youth_plot

Let’s consider just observed values for the Native American subgroup. The first value in time was (\(x_1\) = time point 1) 24.4, then (\(x_2\)) 28.8 at the next time point, then (\(x_3\)) 27.0, then (\(x_4\)) 26.3, then (\(x_5\)) 25.8, and finally (\(x_6\)) 23.9.

If we were to calculate the Mann-Kendall statistic manually we could do the following:

\[ S = sign(x_2−x_1) + sign(x_3-x_1) + sign(x_4-x_1) + sign(x_5-x_1) + sign(x_6-x_1) +\]

\[ sign(x_3-x_2) + sign(x_4-x_2) + sign(x_5-x_2) + sign(x_6-x_2) +\]

\[sign(x_4-x_3) + sign(x_5-x_3) + sign(x_6-x_3) +\]

\[sign(x_5-x_4) + sign(x_6-x_4) +\]

\[sign(x_6-x_5)\]

Thus to calculate \(S\) manually in R we could do like so:

x1 = 24.4
x2 = 28.8
x3 = 27
x4 = 26.3
x5 = 25.8
x6 = 23.9

sign(x2-x1) + sign(x3-x1) + sign(x4-x1) + sign(x5-x1) + sign(x6-x1) +
sign(x3-x2) + sign(x4-x2) + sign(x5-x2) + sign(x6-x2) +sign(x4-x3) + sign(x5-x3) + sign(x6-x3) +sign(x5-x4) + sign(x6-x4) + sign(x6-x5)
[1] -7

The result is -7.

The \(\text{var}(S)\) would be calculated like so (since we have \(n\) = 6 observations and no ties):

\[ \text{var}(S)=\frac{1}{18}(6(6−1)(2(6)+5)−\sum_{p-1}^{g}0(0−1)(2(0)+5))\] \[ \text{var}(S)=\frac{1}{18}(30)(17)−0 =1.666667(17) = 28.33333\]

Finally, we calculate the standardized score \(Z_{MK}\) (with \(S\) < 0):

\(Z_{MK} = \frac{S+1}{\sqrt{VAR(S)}}\) \(Z_{MK} = \frac{-7+1}{\sqrt{28.33333}} = \frac{-6}{5.322906} = -1.127\)

As we noted above, the \(Z_{MK}\) is the specific Mann-Kendall version of the Z score. This allows us to use a variety of data (which maybe normally distributed or not) and create a standard statistic about the data that is normally distributed.

Using the \(Z_{MK}\) Z score, we can use a standard \(Z\) table to determine the \(p\)-value.

The \(p\)-value tells us the probability of observing a test statistic as or more extreme than the one we observed if the null hypothesis (that there is no trend) is true. If the \(p\)-value is small, say less than 0.05, then our observed \(Z_{MK}\) statistic is pretty rare if the null hypothesis is true. This would lead us to reject the null hypothesis and conclude that there is indeed a trend in the data. We will talk more about \(p\)-values, and in particular our choice of 0.05 as a comparison for the \(p\)-value, a little later. See this case study for more information about probability and hypothesis testing.

OK back to our example. Let’s figure out what our \(p\)-value is using the standard \(Z\) score table.

Using the table below, in the the \(Z\) column on the far left, we find a row that says -1.1 and because our \(Z\) value is -1.127 we round this to -1.13 and then find the column for 0.03 to account for the value that is second after decimal place for our \(Z\) score. We see that the value where this row and this column intersect is 0.1292. This is the \(p\)-value, that indicates the probability of having a \(Z\) score that is more extreme (on the negative side) of the \(Z\) score normal distribution or the colored portion of the distribution in the figure below. In other words, there is a 13% chance of observing data with a more negative \(Z\) score (and also a downward trend as the sign of the \(Z_{MK}\) is determined by the \(S\) score) simply by chance alone.

[source]

Since we do not know what type of trend we are looking for (thus we are using what is called a two-sided hypothesis - with the the alternative hypothesis or \(H_{a}\) being that either a positive or negative trend exists), we can use this table to determine that the \(p\)-value is ~.1292 for a one-sided hypothesis (specifically testing that a negative trend exists or testing that a positive trend exists) and multiply this by 2 to get a \(p\)-value of approximately ~ 0.26 for a two-sided hypothesis.

See this article for more information about one-sided and two-sided tests.

Thus in other words, there is a 26% chance of observing a more positive or more negative z score by chance alone. This is a pretty high chance!

Thus visualize this, our two-sided test looks for the areas on the probability distribution of the Z score that are more positive or more negative for both signs of the \(Z\) value:

Alternatively, we can use this calculator, which gives us a more precise \(p\)-value of 0.26 for the two-sided hypothesis.

Either way, our \(p\)-value of ~ 0.26 suggests that we do not have a monotonic trend as our \(p\)-value is much larger than the typical significance threshold of 0.05. The idea here is that it is acceptable that there is a 5% probability that our observation occurred simply due to chance.

In our case there is not enough evidence to reject the null hypothesis. Thus we conclude that there is likely no trend.

M-K test implementation in R


We can also accomplish this with the MannKendall() function of the Kendall package. This function requires a vector of data for which a trend may be observed. See the documentation for this function of the Kendall package for more details.

M-K trend test for Native Americans


In the following we:

  1. Filter for just the data for the Native American group
  2. Use the pull() function of the dplyr package to get just the vector of this data
  3. Apply the MannKendall() function from the Kendall package
  4. Use the base summary() function to get more information for the results of our Mann-Kendall trend test
major_groups_long %>%
  filter(Gender == "All", Race_Ethnicity == "Native American") %>%
  pull(Percent) %>%
  MannKendall(.) %>%
  summary()
Score =  -7 , Var(Score) = 28.33333
denominator =  15
tau = -0.467, 2-sided pvalue =0.25966

Again, just like when we calculated this manually, we see that the score \(S\) is -7. The larger the score, the more indication that there is an increasing or decreasing monotonic relationship. We also again see that the \(p\)-value is roughly = 0.26 and is greater than 0.05, suggesting that we failed to find a monotonic trend in the data.

However, it’s important to note that we only have 6 observations. Thus, we may not have enough observations to observe a trend.

We can also explore whether there is a linear trend using simple linear regression by using the lm() function of the stats package.

First, we visualize the data with Year on the x-axis and Percent of youth disconnection on the y-axis.

major_groups_long %>%
  filter(Gender == "All", Race_Ethnicity == "Native American") %>%
  ggplot(aes(x = Year, y = Percent)) +
    geom_point() 

We are interested in modeling how the percent disconnection changes with time, thus we will use the formula Percent ~ Year in the lm() function, which uses the Percent and Year variables. See this case study for more information about linear regression.

major_groups_long %>%
  filter(Gender == "All",
  Race_Ethnicity == "Native American") %>%
  lm(Percent ~ Year, data = .) %>%
  summary()

Call:
lm(formula = Percent ~ Year, data = .)

Residuals:
      1       2       3       4       5       6 
-2.4332  2.2978  0.8288  0.4597  0.2907 -1.4438 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept) 359.1159   487.3956   0.737    0.502
Year         -0.1655     0.2421  -0.683    0.532

Residual standard error: 1.889 on 4 degrees of freedom
Multiple R-squared:  0.1045,    Adjusted R-squared:  -0.1193 
F-statistic: 0.467 on 1 and 4 DF,  p-value: 0.5319

Based on the coefficients in the output, for each one year change, the mean increase in disconnection rates is -0.1654795

We can also see from the \(p\)-value (Pr(>|t|)), that this relationship is not statistically significant, as it is much larger than 0.05. Again, we are however largely limited by the number of observations in this dataset.

We can visualize the relationship above like so using the geom_smooth() function of the ggplot2 package with the method = "lm" argument to show the linear model.

major_groups_long %>%
  filter(Gender == "All",
  Race_Ethnicity == "Native American") %>%
  ggplot(aes(x = Year, y = Percent)) +
  geom_smooth(method = "lm", color = "red") + 
  geom_point() + 
  scale_x_continuous(breaks = seq(2008, 2018, by = 1),
                     labels = seq(2008, 2018, by = 1),
                     limits = c(2008, 2018)) +
  theme_minimal() +
  labs(title = "Youth Disconnection Rates of Native American Youth",
       subtitle = "2008 - 2017",
       x = "Year",
       y = "Disconnection Rate")

As we can see, that although the line appears to have a negative slope, there is a large amount of uncertainty around the fitted line.


Female Latinx


What about if we look at data that we think we might see a trend according to our data visualizations. The female Latinx data looks as though it may be decreasing:

major_groups_long %>%
  filter(Gender == "Female", Race_Ethnicity == "Latinx") %>%
  pull(Percent) %>%
  MannKendall(.) %>%
  summary()
Score =  -13 , Var(Score) = 28.33333
denominator =  15
tau = -0.867, 2-sided pvalue =0.024171

Indeed this data does shows a trend of declining rates of youth disconnection as the score is again negative and larger and the \(p\)-value is now much less than 0.5 for even the 2-sided hypothesis.

The Z score can be calculated like so:

\(Z_{MK} = \frac{-13+1}{\sqrt{28.33333}} = \frac{-12}{5.322906} = -2.254408\)

The one tailed p-value is half of the 2-sided p-value and is thus roughly 0.012. We just tested two hypotheses, one for the Native Americans and one for the Latinx female population. So we should use the Bonferroni multiple testing correction to adjust our significance threshold \(\alpha\) to be \(.05/(n\text{ tests}) = 0.5/2 = .025\). In this method we divide our significance threshold alpha (generally 0.05) by the number of tests. Why do we need to do this?

Alpha is the amount of risk that you are willing to accept that a statistical test result is actually a false positive, in other words we reject the null hypothesis when the null hypothesis is true. This is also called type 1 error. A significance threshold of .05 is customary and it means that we are accepting a 5% chance that our result is actually a false positive. When we perform multiple tests, the probability of not making a type 1 error would remain the same for each test. But the probability overall of making an error across the tests increases. We can calculate this by multiplying the probabilities for each test(which stays the same for each individual test). See here about why we multiply probabilities together. The more tests we do, the larger the chance that we get a significant result in error. See this case study for more details about multiple testing correction.

Our \(p\)-value for the one sided test was 0.012 which is less than our adjusted significance threshold of .025 for multiple testing. This suggests that indeed there is a decreasing trend for youth disconnection among the Female Latinx population.


Summary


Summary Plot


Now we will create a plot that summarize our findings.

We will create a plot that is just an arrow using geom_segment().

This requires the following arguments : 1) x - x coordinate of starting point of the arrow 2) y - y coordinate of starting point of the arrow 3) xend - x coordinate of end point of the arrow 4) yend - y coordinate of end point if the arrow

Also ggplot requires a data input, we will use this to create the y axis value for the arrow. We will use the aes() function to define the length and location of the arrow.

We need to use the arrow argument with the arrow() function to create an arrow. The lineend and linejoin arguments specify what styles to use. The size() function specifies how thick the arrow is displayed.

See here for different arrow style options.

Then we will use the xlim() function of the ggplot2 package to specify the overall size of the plot relative to the arrow.

arrow_df <- data.frame( y = 1)
arrow_df
  y
1 1
arrowplot<-ggplot(arrow_df, aes(x = .5, y = 1, xend = 1, yend = 1)) +
                geom_segment(arrow = arrow(),
                           lineend = "butt",
                          linejoin = "mitre",
                              size = 3)+
                xlim(0.25, 1.25) + ylim(0,1.5)+
  geom_text( label = "Look at\n subgroups", aes(x = .75, y = .8), size = 5, face = "bold")

arrowplot

Then we will use theme_void() remove the background.

arrowplot <- arrowplot +  theme_void()
arrowplot

We will use the patchwork package to do so.

This package allows us to create formulas for our plot layouts. Thus we can use + to add plots and plot elements like plot_spacer() to add an empty spaceand we can use/` to place plots in different rows on top of one another like a fraction.

Thus we can combine our plots that we previously named and saved as objects.

youth_plot + arrowplot  + (plot_by_gender /asian_subgroup_plot_simp) 

The plot_layout() function allows for specification of the relative heights and widths of the various plot elements. Then, since we will create three columns (distinguished by the + symbols, we need to specify widths and heights for all three.)

We can change aspects about the plots using the + symbol and the theme() function as we have previously used it. We will also add an overall title using the plot_annotation() function.

Finally, we will use the png() function of the grDevices package which is automatically loaded in RStudio sessions to save the plot. We will use the here() function of the here package, to specify that we want to save it in the img directory and call it mainplot.png. We can also use this function to specify the resolution with res and in doing so, we need to save the image with size specifications to make it larger.

plot_by_gender  <-plot_by_gender + theme(axis.text =element_text(size = 12))
asian_subgroup_plot_simp  <-asian_subgroup_plot_simp + theme(axis.text =element_text(size = 12))

png(filename = here::here("img", "mainplot.png"),
    res = 300, width = 15, height = 10, units = "in")
    youth_plot  +
      theme(axis.text =element_text(size = 14, angle = 90)) +
    arrowplot  + 
      (plot_by_gender /
         asian_subgroup_plot_simp)  + 
      plot_layout(widths = c(4.6, 1, 4.65)) &
plot_annotation(title = "Deeper Inspection Reveals Unexpected Differences for Youth Disconnection Among\nGender and Racial/Ethnic Subgroups", 
               theme = theme(plot.title = element_text(size = 22, 
                                                       face = "bold")))

dev.off()
FALSE png 
FALSE   2
knitr::include_graphics(here::here("img", "mainplot.png"))

Synopsis


In this case study we evaluated rates of youth disconnection in the United States between 2008 and 2018, which was defined as individuals between age 16 - 24 who are neither working nor in school". In particular, we focused on evaluating specific subgroups to identify populations that may experience particularly high levels of disconnection. We used summary level data from two Measure of America reports:

  1. The 2019 report called Making the Connection
  2. The 2020 report called A Decade Undone

The data in these reports comes from the American Community Survey(ACS).

We demonstrated how to import data from these PDFs by extracting the text from screen shot images of the tables were were particularly interested in.

It was identified in the 2019 Measure of America annual report that Native American populations particularly experience high rates of youth disconnection. Indeed roughly a quarter of this population appears to experience youth disconnection, which is much higher than the other major racial groups. By performing the Mann-Kendall trend test(https://www.statisticshowto.com/mann-kendall-trend-test/) both manually and using the MannKendall() function of the Kendall package, we demonstrated that, given the available data, there does not appear to be a consistent positive nor a consistent negative trend. This suggests that more prevention and re-engagement strategies should especially focus on this population.

By plotting subgroups within our data, it is clear that there are differences among particular populations. We see that there are much higher levels of disconnection among youths who are male and black as compared to black females.

We also see that youth disconnection rates among Hmong and Cambodian populations are very high, despite rates being quite low for the Asian population in general.

Overall, this case study demonstrates the importance of looking at populations at deeper levels. This is something of great importance facing the field of public health. Where possible data should be collected on a variety of demographic groups to better characterize vulnerable populations.

Suggested Homework


  • For the Asian and Latinx subgroup bar plots made across year, modify these plots to consider gender differences (instead of across time).
  • Taking the plot you made above, modify the plot to facet across years.
  • Find another table in one of the reports to import using the magick package (for example perhaps the data about different states over time in the 2019 report called Making the Connection). Look for differences between groups by plotting the data and evaluating with the Mann-Kendall test.

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             DT_0.20                  
 [3] patchwork_1.1.1           Kendall_2.2              
 [5] forcats_0.5.1             cowplot_1.1.1            
 [7] directlabels_2021.1.13    ggplot2_3.3.5            
 [9] tibble_3.1.6              tidyr_1.1.4              
[11] stringr_1.4.0             dplyr_1.0.7              
[13] magick_2.7.3              tesseract_5.0.0          
[15] pdftools_3.1.1            koRpus.lang.en_0.1-4     
[17] koRpus_0.13-8             sylly_0.1-6              
[19] read.so_0.1.1             wordcountaddin_0.3.0.9000
[21] magrittr_2.0.2            readr_2.1.1              
[23] knitr_1.37                here_1.0.1               

loaded via a namespace (and not attached):
 [1] httr_1.4.2        sass_0.4.0        splines_4.1.2     bit64_4.0.5      
 [5] vroom_1.5.7       jsonlite_1.8.0    bslib_0.3.1       assertthat_0.2.1 
 [9] askpass_1.1       highr_0.9         yaml_2.3.5        remotes_2.4.2    
[13] qpdf_1.1          pillar_1.7.0      lattice_0.20-45   glue_1.6.1       
[17] quadprog_1.5-8    digest_0.6.29     colorspace_2.0-2  htmltools_0.5.2  
[21] Matrix_1.3-4      pkgconfig_2.0.3   purrr_0.3.4       scales_1.1.1     
[25] tzdb_0.2.0        mgcv_1.8-38       farver_2.1.0      generics_0.1.1   
[29] usethis_2.1.5     ellipsis_0.3.2    withr_2.5.0       cli_3.2.0        
[33] crayon_1.5.0      evaluate_0.15     fs_1.5.2          fansi_1.0.2      
[37] nlme_3.1-153      tools_4.1.2       data.table_1.14.2 hms_1.1.1        
[41] lifecycle_1.0.1   munsell_0.5.0     compiler_4.1.2    jquerylib_0.1.4  
[45] rlang_1.0.1       grid_4.1.2        rstudioapi_0.13   rappdirs_0.3.3   
[49] htmlwidgets_1.5.4 crosstalk_1.2.0   labeling_0.4.2    rmarkdown_2.11   
[53] boot_1.3-28       gtable_0.3.0      DBI_1.1.2         curl_4.3.2       
[57] R6_2.5.1          sylly.en_0.1-3    bit_4.0.4         fastmap_1.1.0    
[61] utf8_1.2.2        rprojroot_2.0.2   stringi_1.7.6     parallel_4.1.2   
[65] Rcpp_1.0.8        vctrs_0.3.8       tidyselect_1.1.2  xfun_0.29        

Estimate of RMarkdown Compilation Time:

About 36 - 46 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 Tamar Mendelson for assisting in framing the major direction of the case study.

We would like to acknowledge Qier Meng and Michael Breshock for their contributions to this case study.

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

LS0tDQp0aXRsZTogIk9wZW4gQ2FzZSBTdHVkaWVzOiBEaXNwYXJpdGllcyBpbiBZb3V0aCBEaXNjb25uZWN0aW9uIg0KY3NzOiBzdHlsZS5jc3MNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBpbmNsdWRlczoNCiAgICAgIGluX2hlYWRlcjogR0FfU2NyaXB0LlJodG1sDQogICAgc2VsZl9jb250YWluZWQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgdGhlbWU6IGNvc21vDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL2ltZy9pY29uLWJhaGkucG5nIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDI0MHB4ICFpbXBvcnRhbnQ7DQogIGJhY2tncm91bmQtcmVwZWF0OiBuby1yZXBlYXQ7DQp9DQo8L3N0eWxlPg0KDQoNCjwhLS0gT3BlbiBhbGwgbGlua3MgaW4gbmV3IHRhYi0tPiAgDQo8YmFzZSB0YXJnZXQ9Il9ibGFuayIvPiAgDQoNCjxkaXYgaWQ9Imdvb2dsZV90cmFuc2xhdGVfZWxlbWVudCI+PC9kaXY+DQoNCjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9Jy8vdHJhbnNsYXRlLmdvb2dsZS5jb20vdHJhbnNsYXRlX2EvZWxlbWVudC5qcz9jYj1nb29nbGVUcmFuc2xhdGVFbGVtZW50SW5pdCc+PC9zY3JpcHQ+DQoNCjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4NCmZ1bmN0aW9uIGdvb2dsZVRyYW5zbGF0ZUVsZW1lbnRJbml0KCkgew0KICBuZXcgZ29vZ2xlLnRyYW5zbGF0ZS5UcmFuc2xhdGVFbGVtZW50KHtwYWdlTGFuZ3VhZ2U6ICdlbid9LCAnZ29vZ2xlX3RyYW5zbGF0ZV9lbGVtZW50Jyk7DQp9DQo8L3NjcmlwdD4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCm9wdGlvbnMoY3JheW9uLmVuYWJsZWQgPSBOVUxMKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBUUlVFLCBjb21tZW50ID0gTkEsIGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBvdXQud2lkdGggPSAnOTAlJykNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShtYWdyaXR0cikNCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJiZW5tYXJ3aWNrL3dvcmRjb3VudGFkZGluIiwgdHlwZSA9ICJzb3VyY2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImFsaXN0YWlyZTQ3L3JlYWQuc28iKQ0KbGlicmFyeSh3b3JkY291bnRhZGRpbikNCmxpYnJhcnkocmVhZC5zbykNCg0Kcm1hcmtkb3duOjo6cGVyZl90aW1lcl9yZXNldF9hbGwoKQ0Kcm1hcmtkb3duOjo6cGVyZl90aW1lcl9zdGFydCgicmVuZGVyIikNCmBgYA0KDQoNCiMjIyMgey5vdXRsaW5lIH0NCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI5MDAgcHgifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIm1haW5wbG90LnBuZyIpKQ0KYGBgDQoNCiMjIyMNCg0KIyMjIyB7LmRpc2NsYWltZXJfYmxvY2t9DQoNCioqRGlzY2xhaW1lcioqOiBUaGUgcHVycG9zZSBvZiB0aGUgW09wZW4gQ2FzZSBTdHVkaWVzXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8pIHByb2plY3QgaXMgKip0byBkZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIHZhcmlvdXMgZGF0YSBzY2llbmNlIG1ldGhvZHMsIHRvb2xzLCBhbmQgc29mdHdhcmUgaW4gdGhlIGNvbnRleHQgb2YgbWVzc3ksIHJlYWwtd29ybGQgZGF0YSoqLiBBIGdpdmVuIGNhc2Ugc3R1ZHkgZG9lcyBub3QgY292ZXIgYWxsIGFzcGVjdHMgb2YgdGhlIHJlc2VhcmNoIHByb2Nlc3MsIGlzIG5vdCBjbGFpbWluZyB0byBiZSB0aGUgbW9zdCBhcHByb3ByaWF0ZSB3YXkgdG8gYW5hbHl6ZSBhIGdpdmVuIGRhdGEgc2V0LCBhbmQgc2hvdWxkIG5vdCBiZSB1c2VkIGluIHRoZSBjb250ZXh0IG9mIG1ha2luZyBwb2xpY3kgZGVjaXNpb25zIHdpdGhvdXQgZXh0ZXJuYWwgY29uc3VsdGF0aW9uIGZyb20gc2NpZW50aWZpYyBleHBlcnRzLiANCg0KIyMjIw0KDQojIyMjIHsubGljZW5zZV9ibG9ja30NCg0KVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmNpYWwgMy4wIFsoQ0MgQlktTkMgMy4wKV0oaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLzMuMC91cy8pICBVbml0ZWQgU3RhdGVzIExpY2Vuc2UuDQoNCiMjIyMNCg0KIyMjIyB7LnJlZmVyZW5jZV9ibG9ja30NCg0KVG8gY2l0ZSB0aGlzIGNhc2Ugc3R1ZHkgcGxlYXNlIHVzZToNCg0KV3JpZ2h0LCBDYXJyaWUgYW5kIE9udGl2ZXJvcywgTWljaGFlbCBhbmQgSmFnZXIsIExlYWggYW5kIFRhdWIsIE1hcmdhcmV0IGFuZCBIaWNrcywgU3RlcGhhbmllIEMuICgyMDIwKS4gW2h0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLXlvdXRoLWRpc2Nvbm5lY3Rpb24tY2FzZS1zdHVkeV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MteW91dGgtZGlzY29ubmVjdGlvbi1jYXNlLXN0dWR5KS4gRGlzcGFyaXRpZXMgaW4gWW91dGggRGlzY29ubmVjdGlvbi4NCg0KIyMjIw0KDQpUbyBhY2Nlc3MgdGhlIEdpdEh1YiByZXBvc2l0b3J5IGZvciB0aGlzIGNhc2Ugc3R1ZHkgc2VlIGhlcmU6IGh0dHBzOi8vZ2l0aHViLmNvbS8vb3BlbmNhc2VzdHVkaWVzL29jcy1icC15b3V0aC1kaXNjb25uZWN0aW9uLiANCg0KWW91IG1heSBhbHNvIGFjY2VzcyBhbmQgZG93bmxvYWQgdGhlIGRhdGEgdXNpbmcgb3VyIGBPQ1NkYXRhYCBwYWNrYWdlLiBUbyBsZWFybiBtb3JlIGFib3V0IHRoaXMgcGFja2FnZSBpbmNsdWRpbmcgZXhhbXBsZXMsIHNlZSB0aGlzIFtsaW5rXShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL09DU2RhdGEpLiBIZXJlIGlzIGhvdyB5b3Ugd291bGQgaW5zdGFsbCB0aGlzIHBhY2thZ2U6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiT0NTZGF0YSIpDQpgYGANCg0KVGhpcyBjYXNlIHN0dWR5IGlzIHBhcnQgb2YgYSBzZXJpZXMgb2YgcHVibGljIGhlYWx0aCBjYXNlIHN0dWRpZXMgZm9yIHRoZSBbQmxvb21iZXJnIEFtZXJpY2FuIEhlYWx0aCBJbml0aWF0aXZlXShodHRwczovL2FtZXJpY2FuaGVhbHRoLmpodS5lZHUvb3Blbi1jYXNlLXN0dWRpZXMpLiAgDQoNCioqKg0KDQpUaGUgdG90YWwgcmVhZGluZyB0aW1lIGZvciB0aGlzIGNhc2Ugc3R1ZHkgaXMgY2FsY3VsYXRlZCB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKSBhbmQgc2hvd24gYmVsb3c6IA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnJlYWR0YWJsZSA9IHRleHRfc3RhdHMoImluZGV4LlJtZCIpICMgcHJvZHVjaW5nIHJlYWRpbmcgdGltZSBtYXJrZG93biB0YWJsZQ0KcmVhZHRpbWUgPSByZWFkLnNvOjpyZWFkLm1kKHJlYWR0YWJsZSkgJT4lIGRwbHlyOjpzZWxlY3QoTWV0aG9kLCBrb1JwdXMpICU+JSAjIHJlYWRpbmcgdGFibGUgaW50byBkYXRhZnJhbWUsIHNlbGVjdGluZyByZWxldmFudCBmYWN0b3JzDQogIGRwbHlyOjpmaWx0ZXIoTWV0aG9kID09ICJSZWFkaW5nIHRpbWUiKSAlPiUgIyBkcm9wcGluZyB1bm5lY2Vzc2FyeSByb3dzDQogIGRwbHlyOjptdXRhdGUoa29ScHVzID0gcGFzdGUocm91bmQoYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfc3BsaXQoa29ScHVzLCAiICIpW1sxXV1bMV0pKSwgIm1pbnV0ZXMiKSkgJT4lICMgcm91bmRpbmcgcmVhZGluZyB0aW1lIGVzdGltYXRlDQogIGRwbHlyOjptdXRhdGUoTWV0aG9kID0gImtvUnB1cyIpICU+JSBkcGx5cjo6cmVsb2NhdGUoa29ScHVzLCAuYmVmb3JlID0gTWV0aG9kKSAlPiUgZHBseXI6OnJlbmFtZShgUmVhZGluZyBUaW1lYCA9IGtvUnB1cykgIyByZW9yZ2FuaXppbmcgdGFibGUNCmtuaXRyOjprYWJsZShyZWFkdGltZSwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNCioqUmVhZGFiaWxpdHkgU2NvcmU6ICoqDQoNCkEgcmVhZGFiaWxpdHkgaW5kZXggZXN0aW1hdGVzIHRoZSByZWFkaW5nIGRpZmZpY3VsdHkgbGV2ZWwgb2YgYSBwYXJ0aWN1bGFyIHRleHQuIEZsZXNjaC1LaW5jYWlkLCBGT1JDQVNULCBhbmQgU01PRyBhcmUgdGhyZWUgY29tbW9uIHJlYWRhYmlsaXR5IGluZGljZXMgdGhhdCB3ZXJlIGNhbGN1bGF0ZWQgZm9yIHRoaXMgY2FzZSBzdHVkeSB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKS4gVGhlc2UgaW5kaWNlcyBwcm92aWRlIGFuIGVzdGltYXRpb24gb2YgdGhlIG1pbmltdW0gcmVhZGluZyBsZXZlbCByZXF1aXJlZCB0byBjb21wcmVoZW5kIHRoaXMgY2FzZSBzdHVkeSBieSBncmFkZSBhbmQgYWdlLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpydCA9IHdvcmRjb3VudGFkZGluOjpyZWFkYWJpbGl0eSgiaW5kZXguUm1kIiwgcXVpZXQ9VFJVRSkgIyBwcm9kdWNpbmcgcmVhZGFiaWxpdHkgbWFya2Rvd24gdGFibGUNCmRmID0gcmVhZC5zbzo6cmVhZC5tZChydCkgJT4lIGRwbHlyOjpzZWxlY3QoaW5kZXgsIGdyYWRlLCBhZ2UpICU+JSAgIyByZWFkaW5nIHRhYmxlIGludG8gZGF0YWZyYW1lLCBzZWxlY3RpbmcgcmVsZXZhbnQgZmFjdG9ycw0KICB0aWR5cjo6ZHJvcF9uYSgpICU+JSBkcGx5cjo6bXV0YXRlKGdyYWRlID0gcm91bmQoYXMubnVtZXJpYyhncmFkZSkpLCAjIGRyb3BwaW5nIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcywgcm91bmRpbmcgYWdlIGFuZCBncmFkZSBjb2x1bW5zDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlID0gcm91bmQoYXMubnVtZXJpYyhhZ2UpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCmtuaXRyOjprYWJsZShkZiwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNClBsZWFzZSBoZWxwIHVzIGJ5IGZpbGxpbmcgb3V0IG91ciBzdXJ2ZXkuDQoNCg0KPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsganVzdGlmeS1jb250ZW50OiBjZW50ZXI7Ij48aWZyYW1lIHNyYz0iaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZm9ybXMvZC9lLzFGQUlwUUxTZnBONEZOM0tFTHFCTkVnZjJBdHBpN1d5N05xeTJiZVNrRlFJTkw3WTVzQU1WNV93L3ZpZXdmb3JtP2VtYmVkZGVkPXRydWUiIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcwMCIgZnJhbWVib3JkZXI9IjAiIG1hcmdpbmhlaWdodD0iMCIgbWFyZ2lud2lkdGg9IjAiPkxvYWRpbmfigKY8L2lmcmFtZT48L2Rpdj4NCg0KDQojIyAqKk1vdGl2YXRpb24qKg0KKioqIA0KDQpGaXJzdCwgbGV0J3MgZGlzY3VzcyB0aGUgbWVhbmluZyBvZiB0aGUgdGVybSAqKiJ5b3V0aCBkaXNjb25uZWN0aW9uIioqLiANCg0KQWNjb3JkaW5nIHRvIFtNZWFzdXJlIG9mIEFtZXJpY2FdKGh0dHBzOi8vd3d3LnNzcmMub3JnL3Byb2dyYW1zL3ZpZXcvbW9hLykgKGEgbm9ucGFydGlzYW4gcHJvamVjdCBvZiB0aGUgbm9ucHJvZml0IFtTb2NpYWwgU2NpZW5jZSBSZXNlYXJjaCBDb3VuY2lsXShodHRwczovL3d3dy5zc3JjLm9yZy8pIHRoYXQgaXMgZm9jdXNlZCBvbiBvcHBvcnR1bml0eSBpbiB0aGUgVW5pdGVkIFN0YXRlcyksIGRpc2Nvbm5lY3RlZCB5b3V0aCBhcmU6DQoNCj4gInlvdW5nIHBlb3BsZSBiZXR3ZWVuIHRoZSBhZ2VzIG9mICoqMTYgYW5kIDI0Kiogd2hvIGFyZSAqKm5laXRoZXIgd29ya2luZyBub3IgaW4gc2Nob29sKioiDQoNClRoZSBncm91cCBzdGF0ZXMgdGhhdCBzdWNoIGRpc2Nvbm5lY3Rpb24gaGluZGVycyB0aGVzZSBpbmRpdmlkdWFscyB0byBhY3F1aXJlIHNraWxscyBhbmQgY3JlYXRlIHJlbGF0aW9uc2hpcHMgbmVjZXNzYXJ5IHRvIGhhdmUgYSBzdWNjZXNzZnVsIGFkdWx0aG9vZC4gDQoNClRoZSBncm91cCBnb2VzIG9uIHRvIHN0YXRlIHRoYXQ6DQoNCj4gInBlb3BsZSB3aG8gZXhwZXJpZW5jZSBhIHBlcmlvZCBvZiBkaXNjb25uZWN0aW9uIGFzIHlvdW5nIGFkdWx0cyBnbyBvbiB0byAqKmVhcm4gbGVzcyoqIGFuZCBhcmUgKipsZXNzIGxpa2VseSoqIHRvIGJlICoqZW1wbG95ZWQsIG93biBhIGhvbWUsIG9yIHJlcG9ydCBnb29kIGhlYWx0aCoqIGJ5IHRoZSB0aW1lIHRoZXkgcmVhY2ggdGhlaXIgdGhpcnRpZXMiDQoNCkRpc2Nvbm5lY3RlZCB5b3V0aCBhcmUgYWxzbyByZWZlcnJlZCB0byBhcyAqKm9wcG9ydHVuaXR5IHlvdXRoKiosIHdoaWNoIGhhcyB0aGUgYWRkZWQgcG9zaXRpdmUgY29ubm90YXRpb24gdGhhdCBwcm9tb3Rpbmcgc3VjaCBpbmRpdmlkdWFscyBjYW4gYmUgYmVuZWZpY2lhbCBub3Qgb25seSBmb3IgdGhlc2UgaW5kaXZpZHVhbHMsIGJ1dCBhbHNvIGZvciB0aGVpciBjb21tdW5pdGllcyBhbmQgZm9yIHNvY2lldHkuIA0KDQoqKkdvb2QgbmV3cyoqOiBBY2NvcmRpbmcgdG8gdGhpcyBbcmVwb3J0XShodHRwczovL3NzcmMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vbW9hL01ha2luZyUyMHRoZSUyMENvbm5lY3Rpb24ucGRmKSwgdGhlIHlvdXRoIGRpc2Nvbm5lY3Rpb24gaXMgZ2VuZXJhbGx5IHNob3dpbmcgZGVjcmVhc2luZyB0cmVuZHMgZm9yIHRoZSBwYXN0IDcgeWVhcnMuIA0KDQoqKkJhZCBuZXdzKio6IFRoZSBzYW1lIHJlcG9ydCBzaG93cyAqKnJhY2lhbCBhbmQgZXRobmljIGRpc3Bhcml0aWVzKiosIHdoZXJlIHNvbWUgZ3JvdXBzIGFyZSBzaG93aW5nICoqaW5jcmVhc2VkIHJhdGVzIG9mIGRpc2Nvbm5lY3Rpb24qKi4NCg0KSW4gdGhpcyBjYXNlIHN0dWR5LCB3ZSB3aWxsIGV4cGFuZCBiZXlvbmQgdGhlIFtNZWFzdXJlIG9mIEFtZXJpY2EgYW5udWFsIHJlcG9ydF0oaHR0cHM6Ly9zc3JjLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tL21vYS9NYWtpbmclMjB0aGUlMjBDb25uZWN0aW9uLnBkZikgdG8gdGFrZSBhIGRlZXBlciBsb29rIGF0IGRpZmZlcmVuY2VzIGluIGRpc2Nvbm5lY3Rpb24gYmV0d2VlbiBkaWZmZXJlbnQgc3ViZ3JvdXBzIG9mIHlvdXRocy4gDQpJZGVudGlmeWluZyB5b3V0aHMgcGFydGljdWxhcmx5IGF0IHJpc2sgb3IgZGlzY29ubmVjdGVkLCBjYW4gaGVscCBpbmZvcm0gdGhlIGRlc2lnbiBvZiB0YXJnZXRlZCBwcmV2ZW50aW9uIGFuZCByZS1lbmdhZ2VtZW50IHN0cmF0ZWdpZXMuDQpUbyBkbyB0aGlzLCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBbYXJ0aWNsZV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjI0MzQ0Ni8pIGFzIG91ciBtb3RpdmF0aW9uIGZvciB0aGlzIGNhc2Ugc3R1ZHkuIA0KDQojIyMjIHsucmVmZXJlbmNlX2Jsb2NrfQ0KDQpNZW5kZWxzb24sIFQuLCBNbWFyaSwgSy4sIEJsdW0sIFIuIFcuLCBDYXRhbGFubywgUi4gRi4gJiBCcmluZGlzLCBDLiBELiBPcHBvcnR1bml0eSBZb3V0aDogSW5zaWdodHMgYW5kIE9wcG9ydHVuaXRpZXMgZm9yIGEgUHVibGljIEhlYWx0aCBBcHByb2FjaCB0byBSZWVuZ2FnZSBEaXNjb25uZWN0ZWQgVGVlbmFnZXJzIGFuZCBZb3VuZyBBZHVsdHMuICpQdWJsaWMgSGVhbHRoIFJlcCogMTMzLCA1NFMtNjRTICgyMDE4KS4NCg0KIyMjIw0KIA0KVGhlIFthcnRpY2xlXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM2MjQzNDQ2LykgZGVzY3JpYmVzIHN0cmF0ZWdpZXMgZm9yIHByZXZlbnRpb24gb2YgZGlzY29ubmVjdGlvbiBhbmQgcmUtZW5nYWdlbWVudCBvZiBkaXNjb25uZWN0ZWQgeW91dGggYW5kIGhvdyBzdWNoIGludGVydmVudGlvbnMgY291bGQgZ3JlYXRseSBwb3NpdGl2ZWx5IGltcGFjdCBvcHBvcnR1bml0eSB5b3V0aCBmb3IgdGhlIGVudGlyZSB0cmFqZWN0b3J5IG9mIHRoZWlyIGxpdmVzIGFuZCBmb3IgZnV0dXJlIGdlbmVyYXRpb25zLiANCkl0IGFsc28gcG9pbnRzIG91dCB0aGF0IGluZGVlZCB0aGVyZSBhcmUgZGlzcGFyaXRpZXMgYW1vbmcgZGlmZmVyZW50IHJhY2lhbC9ldGhuaWMgZ3JvdXBzLg0KDQoNCiMjICoqTWFpbiBRdWVzdGlvbnMqKg0KKioqIA0KDQojIyMjIHsubWFpbl9xdWVzdGlvbl9ibG9ja30NCjxiPjx1PiBPdXIgbWFpbiBxdWVzdGlvbnM6IDwvdT48L2I+DQoNCjEpIEhvdyBoYXZlIHlvdXRoIGRpc2Nvbm5lY3Rpb24gcmF0ZXMgaW4gQW1lcmljYW4geW91dGggY2hhbmdlZCBzaW5jZSAyMDA4PyAgIA0KMikgSW4gcGFydGljdWxhciwgaG93IGhhcyB0aGlzIGNoYW5nZWQgZm9yIGRpZmZlcmVudCBnZW5kZXIgYW5kIGV0aG5pYyBncm91cHM/IEFyZSBhbnkgZ3JvdXBzIHBhcnRpY3VsYXJseSBkaXNjb25uZWN0ZWQ/IA0KDQojIyMjDQoNCiMjICoqTGVhcm5pbmcgT2JqZWN0aXZlcyoqIA0KKioqIA0KDQpJbiB0aGlzIGNhc2Ugc3R1ZHksIHdlIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvIGltcG9ydCBhbmQgd3JhbmdsZSBkYXRhIGF2YWlsYWJsZSBpbiBhIDx1PioqUCoqPC91Pm9ydGFibGUgPHU+KipEKio8L3U+b2N1bWVudCA8dT4qKkYqKjwvdT5vcm1hdCAoKipQREYqKikuIFdlIHdpbGwgZXNwZWNpYWxseSBmb2N1cyBvbiB1c2luZyBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zIGZyb20gdGhlIFtgdGlkeXZlcnNlYF0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pLCBzdWNoIGFzIGBkcGx5cmAsIGBnZ3Bsb3QyYC4gVGhlIHRpZHl2ZXJzZSBpcyBhIGxpYnJhcnkgb2YgcGFja2FnZXMgY3JlYXRlZCBieSBSU3R1ZGlvLiBXaGlsZSBzb21lIHN0dWRlbnRzIG1heSBiZSBmYW1pbGlhciB3aXRoIHByZXZpb3VzIFIgcHJvZ3JhbW1pbmcgcGFja2FnZXMsIHRoZXNlIHBhY2thZ2VzIG1ha2UgZGF0YSBzY2llbmNlIGluIFIgbW9yZSBsZWdpYmxlIGFuZCBpbnR1aXRpdmUuDQoNClRoZSBza2lsbHMsIG1ldGhvZHMsIGFuZCBjb25jZXB0cyB0aGF0IHN0dWRlbnRzIHdpbGwgYmUgZmFtaWxpYXIgd2l0aCBieSB0aGUgZW5kIG9mIHRoaXMgY2FzZSBzdHVkeSBhcmU6DQoNCjx1PioqRGF0YSBTY2llbmNlIExlYXJuaW5nIE9iamVjdGl2ZXM6Kio8L3U+DQoNCjEuIEltcG9ydGluZyB0ZXh0IGZyb20gUERGIGZpbGVzIHVzaW5nIGltYWdlcyBhbmQgdGhlIGBtYWdpY2tgIHBhY2thZ2UgIA0KMi4gQXBwbHkgYWN0aW9uIHZlcmJzIGluIGBkcGx5cmAgZm9yIGRhdGEgd3JhbmdsaW5nICANCjMuIEhvdyB0byByZXNoYXBlIGRhdGEgYnkgcGl2b3RpbmcgYmV0d2VlbiAibG9uZyIgYW5kICJ3aWRlIiBmb3JtYXRzIGFuZCBzZXBhcmF0aW5nIGNvbHVtbnMgaW50byBhZGRpdGlvbmFsIGNvbHVtbnMgKGB0aWR5cmApICANCjQuIEhvdyB0byBmaWxsIGluIGRhdGEgYmFzZWQgb24gcHJldmlvdXMgdmFsdWVzIChgdGlkeXJgKQ0KNS4gSG93IHRvIGNyZWF0ZSBkYXRhIHZpc3VhbGl6YXRpb25zIHdpdGggYGdncGxvdDJgIHRoYXQgYXJlIGluIGEgc2ltaWxhciBzdHlsZSB0byBhbiBleGlzdGluZyBpbWFnZSAgICANCjYuIEhvdyB0byBhZGQgaW1hZ2VzIHRvIHBsb3RzIHVzaW5nIGBjb3dwbG90YA0KNy4gSG93IHRvIGNyZWF0ZSBlZmZlY3RpdmUgYmFyIHBsb3RzIHRvIGZvciBtdWx0aXBsZSBjb21wYXJpc29ucywgaW5jbHVkaW5nIGFkZGluZyBnYXBzIGJldHdlZW4gYmFycyBpbiBiYXIgcGxvdHMsIGFkZGluZyBmaWd1cmUgbGVnZW5kcyB0byB0aGUgcGxvdCBhcmVhLCBhbmQgYWRkaW5nIGNvbXBhcmlzb24gbGluZXMgKGBnZ3Bsb3QyYCkNCg0KPHU+KipTdGF0aXN0aWNhbCBMZWFybmluZyBPYmplY3RpdmVzOioqPC91PiAgDQoNCjEuIEltcGxlbWVudGF0aW9uIG9mIHRoZSBNYW5uLUtlbmRhbGwgdHJlbmQgdGVzdCAgDQoyLiBJbnRlcnByZXRhdGlvbiBvZiB0aGUgTWFubi1LZW5kYWxsIHRyZW5kIHRlc3QgDQozLiBEaWZmZXJlbmNlIGJldHdlZW4gbGluZWFyIHJlZ3Jlc3Npb24gYW5kIE1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0DQoNCg0KYGBge3IsIG91dC53aWR0aCA9ICIyMCUiLCBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbiA9ImNlbnRlciJ9DQppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3RpZHl2ZXJzZS50aWR5dmVyc2Uub3JnL2xvZ28ucG5nIikNCmBgYA0KDQoqKiogDQoNCldlIHdpbGwgYmVnaW4gYnkgbG9hZGluZyB0aGUgcGFja2FnZXMgdGhhdCB3ZSB3aWxsIG5lZWQ6DQoNCmBgYHtyfQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShwZGZ0b29scykNCmxpYnJhcnkodGVzc2VyYWN0KQ0KbGlicmFyeShtYWdpY2spDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkaXJlY3RsYWJlbHMpDQpsaWJyYXJ5KGNvd3Bsb3QpDQpsaWJyYXJ5KGZvcmNhdHMpDQpsaWJyYXJ5KEtlbmRhbGwpDQpsaWJyYXJ5KHBhdGNod29yaykNCmxpYnJhcnkoRFQpDQpsaWJyYXJ5KE9DU2RhdGEpDQpgYGANCg0KPHU+KipQYWNrYWdlcyB1c2VkIGluIHRoaXMgY2FzZSBzdHVkeToqKiA8L3U+DQoNCiBQYWNrYWdlICAgfCBVc2UgaW4gdGhpcyBjYXNlIHN0dWR5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQ0KW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSl7dGFyZ2V0PSJfYmxhbmsifSAgICAgICB8IHRvIGVhc2lseSBsb2FkIGFuZCBzYXZlIGRhdGEgIA0KW3BkZnRvb2xzXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKSAgICAgIHwgdG8gaW1wb3J0IFBERiBkb2N1bWVudHMgIA0KW21hZ2lja10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ2ljay92aWduZXR0ZXMvaW50cm8uaHRtbCNLZXJuZWxfY29udm9sdXRpb24pICAgICAgfCBmb3IgaW1wb3J0aW5nIGltYWdlcyBhbmQgZXh0cmFjdGluZyB0ZXh0IGZyb20gaW1hZ2VzICAgDQpbdGVzc2VyYWN0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGVzc2VyYWN0L3ZpZ25ldHRlcy9pbnRyby5odG1sKSB8IGZvciBleHRyYWN0aW5nIHRleHQgZnJvbSBpbWFnZXMgd2l0aCBgbWFnaWNrYCAgIA0KW2tuaXRyXShodHRwczovL3lpaHVpLm9yZy9rbml0ci8pIHwgIGZvciBzaG93aW5nIGltYWdlcyBpbiByZXBvcnRzICAgIA0KW3JlYWRyXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKSB8IGZvciBzYXZpbmcgZmlsZXMNCltkcGx5cl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gZmlsdGVyLCBzdWJzZXQsIGpvaW4sIGFkZCByb3dzIHRvLCBhbmQgbW9kaWZ5IHRoZSBkYXRhICAgDQpbc3RyaW5ncl0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBtYW5pcHVsYXRlIHN0cmluZ3MgIA0KW21hZ3JpdHRyXShodHRwczovL21hZ3JpdHRyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBwaXBlIHNlcXVlbnRpYWwgY29tbWFuZHMgDQpbdGlkeXJdKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGNoYW5nZSB0aGUgc2hhcGUgb3IgZm9ybWF0IG9mIHRpYmJsZXMgdG8gd2lkZSBhbmQgbG9uZywgdG8gZHJvcCByb3dzIHdpdGggYE5BYCB2YWx1ZXMsIHRvIHNlcGFyYXRlIGEgY29sdW1uIGludG8gYWRkaXRpb25hbCBjb2x1bW5zLCBhbmQgdG8gZmlsbCBvdXQgdmFsdWVzIGJhc2VkIG9uIHByZXZpb3VzIHZhbHVlcyAgIA0KW3RpYmJsZV0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGNyZWF0ZSB0aWJibGVzICAgIA0KW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY3JlYXRlIHBsb3RzICANCltkaXJlY3RsYWJlbHNdKGh0dHA6Ly9kaXJlY3RsYWJlbHMuci1mb3JnZS5yLXByb2plY3Qub3JnL2RvY3MvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gYWRkIGxhYmVscyBkaXJlY3RseSB0byBsaW5lcyBpbiBwbG90cyAgDQpbY293cGxvdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nvd3Bsb3QvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBhZGQgaW1hZ2VzIHRvIHBsb3RzIA0KW2ZvcmNhdHNdKGh0dHBzOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gcmVvcmRlciBmYWN0b3IgZm9yIHBsb3QNCltrZW5kYWxsXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvS2VuZGFsbC9LZW5kYWxsLnBkZikgfCB0byBpbXBsZW1lbnQgdGhlIE1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0IGluIFIgICANCltwYXRjaHdvcmtdKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvcGF0Y2h3b3JrKSB8IHRvIGNvbWJpbmUgcGxvdHMNCltEVF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL0RUL2luZGV4Lmh0bWwpIHwgSW50ZXJhY3RpdmUgdGFibGVzDQpbT0NTZGF0YV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9PQ1NkYXRhKSB8IHRvIGFjY2VzcyBhbmQgZG93bmxvYWQgT0NTIGRhdGEgZmlsZXMNCg0KDQpUaGUgZmlyc3QgdGltZSB3ZSB1c2UgYSBmdW5jdGlvbiwgd2Ugd2lsbCB1c2UgdGhlIGA6OmAgdG8gaW5kaWNhdGUgd2hpY2ggcGFja2FnZSB3ZSBhcmUgdXNpbmcuIFVubGVzcyB3ZSBoYXZlIG92ZXJsYXBwaW5nIGZ1bmN0aW9uIG5hbWVzLCB0aGlzIGlzIG5vdCBuZWNlc3NhcnksIGJ1dCB3ZSB3aWxsIGluY2x1ZGUgaXQgaGVyZSB0byBiZSBpbmZvcm1hdGl2ZSBhYm91dCB3aGVyZSB0aGUgZnVuY3Rpb25zIHdlIHdpbGwgdXNlIGNvbWUgZnJvbS4NCg0KIyMgKipDb250ZXh0KioNCioqKiANCg0KU28gaG93IGRvZXMgeW91dGggZGlzY29ubmVjdGlvbiBoYXBwZW4gYW5kIHdoYXQgaW1wYWN0IGRvZXMgaXQgaGF2ZT8NCg0KDQpUaGVyZSBhcmUgbWFueSBrbm93biAqKnJpc2sgZmFjdG9ycyoqLCB3aGljaCBoYXZlIGJlZW4gaWRlbnRpZmllZCBpbiBhIHZhcmlldHkgb2YgY29udGV4dHMgKGZyb20gZmFtaWx5LCBmcmllbmRzLCBzY2hvb2wsIGNvbW11bml0eSwgc29jaWV0eSkgaW5jbHVkaW5nOiAgDQoNCiAtIHBvdmVydHkgKGRpc2Nvbm5lY3RlZCB5b3V0aCBhcmUgbmVhcmx5IHR3aWNlIGFzIGxpa2VseSB0byBsaXZlIGluIHBvdmVydHkgYW5kIHJlY2VpdmUgTWVkaWNhaWQpICANCiAtIHJhY2lhbC9ldGhuaWMgZGlzcGFyaXRpZXMgKGZpbmRpbmdzIHN1Z2dlc3QgdGhhdCB0aGVzZSBwZXJzaXN0IGV2ZW4gd2hlbiBjb250cm9sbGluZyBmb3IgaW5jb21lKSAgDQogLSByZXNpZGVudGlhbCBlbnZpcm9ubWVudCAoaW4gMjAxNiB3aGlsZSAxMS43JSB3YXMgdGhlIG5hdGlvbmFsIGF2ZXJhZ2UsIDI0JSBvZiBwZW9wbGUgYWdlIDE2LTI0IGluIHRoZSBydXJhbCBTb3V0aCB3ZXJlIGRpc2Nvbm5lY3RlZCkgIA0KIC0gcG9vciBhY2FkZW1pYyBwZXJmb3JtYW5jZSAgDQogLSBwb29yIG1lbnRhbCBoZWFsdGggIA0KIC0gc3Vic3RhbmNlIHVzZSBkaXNvcmRlcnMgIA0KIC0gcGFyZW50YWwgdW5lbXBsb3ltZW50ICANCiAtIHRyYXVtYSBleHBvc3VyZSAgDQogLSBhc3NvY2lhdGlvbiB3aXRoIHNvY2lhbGx5IGRldmlhbnQgcGVlcnMgDQogLSBzY2hvb2wgcG9saWNpZXMgc3VjaCBhcyAib25lIHN0cmlrZSBhbmQgeW91J3JlIG91dCIgLSB3aGljaCBpcyBhIHplcm8gdG9sZXJhbmNlIHNjaG9vbCBleHB1bHNpb24gcG9saWN5IGFuZCBzaG93biB0byBpbmNyZWFzZSBkcm9wb3V0cyBhbmQgaW5jYXJjZXJhdGlvbiByYXRlcw0KIA0KIFRoZXNlIHJpc2sgZmFjdG9ycyBtYWtlIGl0IG1vcmUgbGlrZWx5IGZvciB5b3VuZyBwZW9wbGUgdG8gbWlzcyBvdXQgb24gZWR1Y2F0aW9uLCB0cmFpbmluZywgYW5kIG5ldHdvcmtpbmcgdGhhdCBjYW4gYWN0IGFzIGEgZm91bmRhdGlvbiBmb3IgYSBzdWNjZXNzZnVsIGNhcmVlci4NCiANClRoZXJlIGFyZSBhbHNvIG1hbnkga25vd24gKipuZWdhdGl2ZSBjb25zZXF1ZW5jZXMqKiBhc3NvY2lhdGVkIHdpdGggeW91dGggZGlzY29ubmVjdGlvbiBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvOg0KDQotIGNocm9uaWMgdW5lbXBsb3ltZW50DQotIHBvdmVydHkNCi0gcG9vciBtZW50YWwgaGVhbHRoIGFuZCBwb29yIGdlbmVyYWwgaGVhbHRoIChpbiBhIDIwMDIgc3R1ZHkgLSB5b3V0aHMgZGlzY29ubmVjdGVkIGZvciA2IG9yIG1vcmUgbW9udGhzIHdlcmUgMyB0aW1lcyBtb3JlIGxpa2VseSB0byBkZXZlbG9wIGRlcHJlc3Npb24gb3Igb3RoZXIgbWVudGFsIGhlYWx0aCBkaXNvcmRlcikNCi0gY3JpbWluYWwgYmVoYXZpb3IgKGluIGEgMjAwMiBzdHVkeSAtIHlvdXRocyBkaXNjb25uZWN0ZWQgZm9yIDYgb3IgbW9yZSBtb250aHMgd2VyZSA1IHRpbWVzIG1vcmUgbGlrZWx5IHRvIGhhdmUgYSBjcmltaW5hbCByZWNvcmQpDQotIGluY2FyY2VyYXRpb24NCi0gZWFybHkgbW9ydGFsaXR5IA0KDQoNCmBgYHtyLCBvdXQud2lkdGg9ICI0MDBweCIsZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJqb24tdHlzb24tYWp6TjJBWU5pMVUtdW5zcGxhc2guanBnIikpDQpgYGANCjxzcGFuPlBob3RvIGJ5IDxhIGhyZWY9Imh0dHBzOi8vdW5zcGxhc2guY29tL0Bqb250eXNvbj91dG1fc291cmNlPXVuc3BsYXNoJmFtcDt1dG1fbWVkaXVtPXJlZmVycmFsJmFtcDt1dG1fY29udGVudD1jcmVkaXRDb3B5VGV4dCI+Sm9uIFR5c29uPC9hPiBvbiA8YSBocmVmPSJodHRwczovL3Vuc3BsYXNoLmNvbS9zL3Bob3Rvcy91bmVtcGxveW1lbnQ/dXRtX3NvdXJjZT11bnNwbGFzaCZhbXA7dXRtX21lZGl1bT1yZWZlcnJhbCZhbXA7dXRtX2NvbnRlbnQ9Y3JlZGl0Q29weVRleHQiPlVuc3BsYXNoPC9hPjwvc3Bhbj4NCg0KRnVydGhlcm1vcmUsIGluIDIwMTIgaXQgd2FzIGVzdGltYXRlZCB0aGF0IGVhY2ggZGlzY29ubmVjdGVkIHlvdXRoIGNvc3RzIHRheHBheWVycyAkMjUwLDAwMCBkdXJpbmcgYSBsaWZldGltZSBkdWUgdG8gbG9zdCB0YXggcmV2ZW51ZSBhbmQgY29zdHMgZm9yIHNvY2lhbCBzZXJ2aWNlcywgaGVhdGggY2FyZSBhbmQgY3JpbWluYWwganVzdGljZS4NCg0KWW91dGggZGlzY29ubmVjdGlvbiBjYW4gYmUgZGVzY3JpYmVkIGFzIGEgY29udGludXVtLCBhcyBzb21lIHlvdXRocyB3aWxsIGJlIGRpc2Nvbm5lY3RlZCBmb3IgYSBicmllZiB0aW1lLCB3aGlsZSBvdGhlcnMgYXJlIGNocm9uaWNhbGx5IGRpc2Nvbm5lY3RlZC4gQWRkaXRpb25hbGx5LCB3aGlsZSBhbiBpbmRpdmlkdWFsIHdobyBpcyBvdXQgb2Ygc2Nob29sIGFuZCB3b3JrIGFuZCBhbHNvIGhhcyBwb29yIHN1cHBvcnQgZnJvbSB0aGUgcmVsYXRpb25zaGlwcyBvZiBvdGhlcnMgbWF5IGJlIGZ1cnRoZXIgZGlzY29ubmVjdGVkIHRoYW4gYW4gaW5kaXZpZHVhbCB3aG8gaGFzIHNvY2lhbCBzdXBwb3J0Lg0KDQpIZXJlIGlzIGFuIGlsbHVzdHJhdGlvbiBvZiByaXNrIGZhY3RvcnMsIHByb3RlY3RpdmUgZmFjdG9ycyBhbmQgdGhlIGNvbnRpbnV1bSBvZiBkaXNjb25uZWN0aW9uOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiODAwIHB4In0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJyaXNrX2ZhY3RvcnMucG5nIikpDQpgYGANCg0KIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM2MjQzNDQ2L3BkZi8xMC4xMTc3XzAwMzMzNTQ5MTg3OTkzNDQucGRmKSANCg0KDQojIyMgKipTdHJhdGVnaWVzIHRvIG1pdGlnYXRlIHlvdXRoIGRpc2Nvbm5lY3Rpb24qKg0KKioqDQoNCk1hbnkgcHJvZ3JhbXMgaGF2ZSBpZGVudGlmaWVkIHVzZWZ1bCBzdHJhdGVnaWVzIGluIHJlLWVuZ2FnaW5nIGRpc2Nvbm5lY3RlZCB5b3V0aCBvciBwcmV2ZW50aW5nIGRpc2Nvbm5lY3Rpb24gb2YgeW91dGguIA0KDQpHZW5lcmFsbHkgc3BlYWtpbmcsIG1vc3QgcHJvZ3JhbXMgZm9jdXMgb24gcmUtZW5nYWdlbWVudCBzdHJhdGVnaWVzLCBob3dldmVyLCBwcmV2ZW50aW9uIHN0cmF0ZWdpZXMgYXJlIGxpa2VseSB0byBiZSBqdXN0IGFzIGltcG9ydGFudC4gDQoNClJlc2VhcmNoIHN1Z2dlc3RzIHRoYXQgYWN0aXZlIGludm9sdmVtZW50IHdpdGggYXQgcmlzayB5b3V0aCBmcm9tIGluZmFuY3kgYW5kIGFjcm9zcyBtdWx0aXBsZSBkZXZlbG9wbWVudGFsIHN0YWdlcyB0aHJvdWdoIHlvdW5nIGFkdWx0aG9vZCB3b3VsZCBiZSB0aGUgbW9zdCBiZW5lZmljaWFsLg0KDQpJbiBmYWN0LCB0aGUgcXVhbGl0eSBvZiBwYXJlbnRhbCBjYXJlLWdpdmluZyBvZiBpbmZhbnRzIGFnZSA2LTI0IG1vbnRocyBoYXMgYWN0dWFsbHkgYmVlbiBzaG93biB0byBiZSBhIHByZWRpY3RvciBvZiBoaWdoIHNjaG9vbCBkcm9wb3V0IHJhdGVzISBUaHVzIGVhcmx5IGludGVydmVudGlvbnMgbWF5IGJlIHZlcnkgaW1wb3J0YW50IGFuZCBjb25zaXN0ZW50IGNvbnRpbnVhbCBlbmdhZ2VtZW50IG1heSBwcmV2ZW50IGZ1cnRoZXIgZGlzY29ubmVjdGlvbiBvZiB5b3V0aHMuDQoNClByZXZlbnRpb24gc3RyYXRlZ2llcyBpbmNsdWRlOiAgDQoNCi0gUHJlc2Nob29sICANCi0gW1RoZSBHb29kIEJlaGF2aW9yIEdhbWVdKGh0dHBzOi8vd3d3Lmdvb2RiZWhhdmlvcmdhbWUub3JnL2dvb2QtYmVoYXZpb3ItZ2FtZS13aGF0LWlzLWl0KSAgDQotIFN0cmVuZ3RoZW5pbmcgZmFtaWx5IGFuZCBjb21tdW5pdHkgY29ubmVjdGlvbnMgIA0KLSBQcm9tb3RpbmcgYWNhZGVtaWMgYW5kIGNhcmVlciBlbmdhZ2VtZW50ICANCi0gTGlmZSBza2lsbHMgdHJhaW5pbmcgZm9yIHlvdXRocyBhbmQgZmFtaWxpZXMgIA0KLSBFZHVjYXRpb24gYWJvdXQgc3Vic3RhbmNlIGFidXNlICANCi0gW0NvZ25pdGl2ZSBiZWhhdmlvcmFsIHRoZXJhcHldKGh0dHBzOi8vd3d3LmFwYS5vcmcvcHRzZC1ndWlkZWxpbmUvcGF0aWVudHMtYW5kLWZhbWlsaWVzL2NvZ25pdGl2ZS1iZWhhdmlvcmFsKSAgICANCg0KDQojIyMjIHsuZW1waGFzaXNfYmxvY2t9DQoNCldhbnQgdG8gbGVhcm4gbW9yZSBhYm91dCBob3cgdG8gcHJldmVudCBhbmQgbWl0aWdhdGUgeW91dGggZGlzY29ubmVjdGlvbj8gIA0KT3IgZG8geW91IGtub3cgeW91dGhzIHdobyBhcmUgZGlzY29ubmVjdGVkPyAgDQoNClNlZSBbdGhlIHByb2dyYW0gZGlyZWN0b3J5IGF0IHlvdXRoLmdvdl0oaHR0cHM6Ly95b3V0aC5nb3YvZXZpZGVuY2UtaW5ub3ZhdGlvbi9wcm9ncmFtLWRpcmVjdG9yeT9rZXl3b3Jkcz0mZmllbGRfcGRfZmFjdG9yc19yaXNrc190aWQ9NDEzJmZpZWxkX3BkX2ZhY3RvcnNfcHJvdGVjdGl2ZV90aWQ9QWxsKSBhbmQgW3RoaXMgcHJvZ3JhbSBsaXN0aW5nXShodHRwczovL2dvYy5tYXJ5bGFuZC5nb3Yvd3AtY29udGVudC91cGxvYWRzL3NpdGVzLzgvMjAxNS8xMC9Qcm9ncmFtLU1vZGVscy1mb3ItU2VydmluZy1PcHBvcnR1bml0eS1Zb3V0aC5wZGYpIGZvY3VzZWQgb24gTWFyeWxhbmQgYnV0IGluY2x1ZGluZyBvdGhlciBsb2NhdGlvbnMgZm9yIGxpc3RpbmdzIG9mIHByb2dyYW1zIGRlZGljYXRlZCB0byByZS1lbmdhZ2luZyBkaXNjb25uZWN0ZWQgeW91dGggb3IgcHJldmVudGluZyBkaXNjb25uZWN0aW9uLg0KDQpBbHNvLCBzZWUgW1RoZSBDZW50ZXIgZm9yIENvbW11bml0aWVzIFRoYXQgQ2FyZV0oaHR0cHM6Ly93d3cuY29tbXVuaXRpZXN0aGF0Y2FyZS5uZXQvKSBhbmQgdGhlDQpbUFJPU1BFUiBwcm9ncmFtXShodHRwczovL2V4dGVuc2lvbi5wc3UuZWR1L3Byb21vdGluZy1zY2hvb2wtY29tbXVuaXR5LXVuaXZlcnNpdHktcGFydG5lcnNoaXBzLXRvLWVuaGFuY2UtcmVzaWxpZW5jZSkgZm9yIHBhcnRpY3VsYXIgZXhhbXBsZXMuDQoNCiMjIyMNCg0KDQpUaGUgc3RhdGlzdGljcyB1c2VkIGluIHRoaXMgc2VjdGlvbiBjYW1lIGZyb20gdGhpcyBbYXJ0aWNsZV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjI0MzQ0Ni8pLg0KDQojIyAqKkxpbWl0YXRpb25zKioNCioqKiANCg0KVGhlcmUgYXJlIHNvbWUgaW1wb3J0YW50IGNvbnNpZGVyYXRpb25zIHJlZ2FyZGluZyB0aGlzIGRhdGEgYW5hbHlzaXMgdG8ga2VlcCBpbiBtaW5kOiANCg0KMS4gVGhpcyBkYXRhIHVzZWQgaW4gdGhlIFtNZWFzdXJlIG9mIEFtZXJpY2FdKGh0dHBzOi8vd3d3LnNzcmMub3JnL3Byb2dyYW1zL3ZpZXcvbW9hLykgcHJvamVjdCBpcyBkZXJpdmVkIGZyb20gW0FtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgKEFDUyldKGh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvcHJvZ3JhbXMtc3VydmV5cy9hY3MpLCB3aGljaCBleGNsdWRlcyBvciB1bmRlci1yZXByZXNlbnRzIGNlcnRhaW4gb3Bwb3J0dW5pdHkgeW91dGggZ3JvdXBzLCBzdWNoIGFzIHlvdXRocyBpbiB0aGUganV2ZW5pbGUganVzdGljZSBzeXN0ZW0sIHlvdXRocyBpbiB0aGUgZm9zdGVyIGNhcmUgc3lzdGVtLCBhbmQgaG9tZWxlc3MgeW91dGhzIGFzIHRoZSBzdXJ2ZXkgaXMgY29uZHVjdGVkIG9uIGhvdXNlaG9sZHMuIEZ1cnRoZXJtb3JlLCB5b3V0aHMgd2hvIG1heSBiZSBtb3JlIGRpc2Nvbm5lY3RlZCBmb3Igb3RoZXIgcmVhc29ucyBiZXNpZGVzIG5vdCBiZWluZyBpbiB3b3JrIG9yIHNjaG9vbCwgc3VjaCBhcyBkZWFsaW5nIHdpdGggdGhlIGFkZGVkIGNoYWxsZW5nZSBvZiBiZWluZyBhIHRlZW5hZ2UgbW90aGVyLCBvciBiZWluZyBhYnVzZWQgYXJlIG5vdCBhdmFpbGFibGUgaW4gdGhpcyBkYXRhc2V0LiBUaHVzLCB0aGlzIGRhdGEgbGlrZWx5IHVuZGVyZXN0aW1hdGVzIHlvdXRoIGRpc2Nvbm5lY3Rpb24gcmF0ZXMuIEZ1cnRoZXJtb3JlLCB0aGUgZGF0YSB1c2VkIGluIHRoZXNlIHJlcG9ydHMgYXJlIGNvbGxlY3RlZCBmcm9tIGh1bWFuIHBhcnRpY2lwYW50czsgdGhpcyBwcmVzZW50cyB0aGUgKnBvdGVudGlhbCogZm9yIFtyZXNwb25zZSBiaWFzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZXNwb25zZV9iaWFzKSwgYXMgdGhlcmUgaXMgdGhlICpwb3RlbnRpYWwqIHRoYXQgcGFydGljaXBhbnRzIGluIHRoZSBbc2FtcGxpbmcgZnJhbWVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NhbXBsaW5nX2ZyYW1lP29sZGZvcm1hdD10cnVlKXt0YXJnZXQ9Il9ibGFuayJ9IG1heSBmb3IgYSB2YXJpZXR5IG9mIHJlYXNvbnMgcmVwb3J0IGluYWNjdXJhdGUgaW5mb3JtYXRpb24uIA0KDQoyLiBEYXRhIGFib3V0IGNlcnRhaW4gZ3JvdXAgW2ludGVyc2VjdGlvbnNdKGh0dHBzOi8vd3d3LnZveC5jb20vdGhlLWhpZ2hsaWdodC8yMDE5LzUvMjAvMTg1NDI4NDMvaW50ZXJzZWN0aW9uYWxpdHktY29uc2VydmF0aXNtLWxhdy1yYWNlLWdlbmRlci1kaXNjcmltaW5hdGlvbikgKG1lYW5pbmcgZm9yIGV4YW1wbGUgaW5kaXZpZHVhbHMgb2YgYSBwYXJ0aWN1bGFyIGdlbmRlciBhbmQgZXRobmljaXR5KSBvciBwYXJ0aWN1bGFyIGdyb3VwcyBpbiBnZW5lcmFsIHN1Y2ggYXMgc3BlY2lmaWMgZXRobmljaXRpZXMgb3IgZ2VuZGVyIG9yIHNleHVhbCBpZGVudGl0eSBncm91cHMgc3VjaCBhcyBbTEdCVFFJQStdKGh0dHBzOi8vd3d3LnVpcy5lZHUvZ2VuZGVyc2V4dWFsaXR5c3R1ZGVudHNlcnZpY2VzL2Fib3V0L2xnYnRxYXRlcm1pbm9sb2d5LykgKGxlc2JpYW4vZ2F5L2Jpc2V4dWFsL3BhbnNleHVhbC90cmFuc2dlbmRlci9nZW5kZXJxdWVlci9xdWVlcixxdWVzdGlvbmluZy9pbnRlcnNleGVkL2FnZW5kZXIvYXNleHVhbCkgaXMgdW5mb3J0dW5hdGVseSBub3QgYXZhaWxhYmxlIGluIHRoZSBkYXRhIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBhbmQgaW4gbW9zdCByZXNlYXJjaCBhYm91dCB0aGlzIHRvcGljLiBMdWNraWx5IGhvd2V2ZXIsIHJlY2VudCB5ZWFycyBvZiB0aGUgW0FDUyBzdXJ2ZXldKGh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvcHJvZ3JhbXMtc3VydmV5cy9hY3MpIGhhcyBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IGEgZ3JlYXRlciBudW1iZXIgb2YgcmFjaWFsIGFuZCBldGhuaWMgZ3JvdXBzIGFuZCByYWNpYWwvZXRobmljIGludGVyc2VjdGlvbnMuDQoNCjMuIFRoZSBzdGF0aXN0aWNhbCBwcm9jZWR1cmVzIHdlIGFyZSB1c2luZyBtYXkgYmUgb3Zlcmx5IHNpbXBsaXN0aWMuICpJbiBhbGwgZGF0YSBhbmFseXNpcywgd2UgbmVlZCB0byBiZSB3YXJ5IGFib3V0IGRlcml2aW5nIG1lYW5pbmcgZnJvbSB0aGUgc3RhdGlzdGljYWwgcHJvY2VkdXJlcyB3ZSB1c2UqLiBGdXJ0aGVybW9yZSwgdGhlIGRhdGEgd2UgYXJlIHVzaW5nIGlzIHN1bW1hcnkgbGV2ZWwgc2hvd2luZyBhdmVyYWdlcyBmb3IgbGFyZ2UgZ3JvdXBzLiBUaHVzLCBpdCBpcyBkaWZmaWN1bHQgdG8ga25vdyBob3cgcmVsaWFibGUgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGFyZSwgYXMgd2UgZG9uJ3QgaGF2ZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdmFyaWFiaWxpdHkgb2YgdGhlIGRhdGEgd2l0aGluIGEgZ3JvdXAuDQoNCjQuIFVzaW5nIGltYWdlIHByb2Nlc3NpbmcgdG9vbHMgY2FuIGJlIHZlcnkgaGVscGZ1bC4gVGhlIG1hbm5lciBpbiB3aGljaCBkYXRhIGlzIG9idGFpbmVkIHdpdGggaW1hZ2UgcHJvY2Vzc2luZyB0b29scyBpcyB3aGF0IHdlIHdvdWxkIGRlc2NyaWJlIGFzIGEgKipibGFjayBib3ggcHJvY2VzcyoqLCA8dT4qYSBwcm9jZXNzIHdpdGgga25vd24gaW5wdXRzIGFuZCBvdXRwdXRzIGJ1dCB1bmtub3duIG1lY2hhbmljcyo8L3U+LiBCZWNhdXNlIHdlIGFyZSB1bmF3YXJlIG9mIGhvdyBvdXIgb3V0cHV0cyBhcmUgZ2VuZXJhdGVkIGZyb20gb3VyIGlucHV0cywgd2UgbmVlZCB0byBiZSB3YXJ5IG9mIHRoZSBvdXRwdXQuIFdpdGggdGhlIHNtYWxsIG91dHB1dCB3ZSBhcmUgY3JlYXRpbmcgaW4gdGhpcyBjYXNlIHN0dWR5LCBhIHZpc3VhbCBpbnNwZWN0aW9uIHNob3VsZCBzdWZmaWNlLiANCg0KDQojIyAqKldoYXQgYXJlIHRoZSBkYXRhPyoqDQoqKiogDQoNCkluIHRoaXMgY2FzZSBzdHVkeSB3ZSB3aWxsIGJlIHVzaW5nIGRhdGEgcmVsYXRlZCB0byB5b3V0aCBkaXNjb25uZWN0aW9uIGZyb20gdGhlIHR3byBmb2xsb3dpbmcgcmVwb3J0cyBmcm9tIHRoZSBbTWVhc3VyZSBvZiBBbWVyaWNhXShodHRwczovL3d3dy5zc3JjLm9yZy9wcm9ncmFtcy92aWV3L21vYS8pICBwcm9qZWN0Og0KDQo+IE1lYXN1cmUgb2YgQW1lcmljYSBpcyBhIG5vbnBhcnRpc2FuIHByb2plY3Qgb2YgdGhlIG5vbnByb2ZpdCBbU29jaWFsIFNjaWVuY2UgUmVzZWFyY2ggQ291bmNpbF0oaHR0cHM6Ly93d3cuc3NyYy5vcmcvKSAgZm91bmRlZCBpbiAyMDA3IHRvIGNyZWF0ZSBlYXN5LXRvLXVzZSB5ZXQgbWV0aG9kb2xvZ2ljYWxseSBzb3VuZCB0b29scyBmb3IgdW5kZXJzdGFuZGluZyB3ZWxsLWJlaW5nIGFuZCBvcHBvcnR1bml0eSBpbiBBbWVyaWNhLiBUaHJvdWdoIHJlcG9ydHMsIGludGVyYWN0aXZlIGFwcHMsIGFuZCBjdXN0b20tYnVpbHQgZGFzaGJvYXJkcywgTWVhc3VyZSBvZiBBbWVyaWNhIHdvcmtzIHdpdGggcGFydG5lcnMgdG8gYnJlYXRoZSBsaWZlIGludG8gbnVtYmVycywgdXNpbmcgZGF0YSB0byBpZGVudGlmeSBhcmVhcyBvZiBoaWdoZXN0IG5lZWQsIHBpbnBvaW50IGxldmVycyBmb3IgY2hhbmdlLCBhbmQgdHJhY2sgcHJvZ3Jlc3Mgb3ZlciB0aW1lLg0KDQoxLiBMZXdpcywgS3Jpc3Rlbi4gW01ha2luZyB0aGUgQ29ubmVjdGlvbjogVHJhbnNwb3J0YXRpb24gYW5kIFlvdXRoIERpc2Nvbm5lY3Rpb25dKGh0dHBzOi8vc3NyYy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS9tb2EvTWFraW5nJTIwdGhlJTIwQ29ubmVjdGlvbi5wZGYpIC4gTmV3IFlvcms6IE1lYXN1cmUgb2YgQW1lcmljYSwgU29jaWFsIFNjaWVuY2UgUmVzZWFyY2ggQ291bmNpbCwgMjAxOS4gIChEYXRhIHVwIHRvIDIwMTcpDQoNCmBgYHtyLCBvdXQud2lkdGg9IjQwMHB4IiwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk1ha2luZ190aGVfQ29ubmVjdGlvbi5wbmciKSkNCmBgYA0KDQoyLiBMZXdpcywgS3Jpc3Rlbi4gW0EgRGVjYWRlIFVuZG9uZTogWW91dGggRGlzY29ubmVjdGlvbiBpbiB0aGUgQWdlIG9mIENvcm9uYXZpcnVzXShodHRwczovL3NzcmMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vbW9hL0FEZWNhZGVVbmRvbmUucGRmKS4gTmV3IFlvcms6IE1lYXN1cmUgb2YgQW1lcmljYSwgU29jaWFsIFNjaWVuY2UgUmVzZWFyY2ggQ291bmNpbCwgMjAyMC4gKERhdGEgdXAgdG8gMjAxOCkNCg0KYGBge3IsIG91dC53aWR0aD0iNDAwcHgiLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiQV9EZWNhZGVfVW5kb25lLnBuZyIpKQ0KYGBgDQoNClRoZSBkYXRhIHVzZWQgaW4gdGhlc2UgcmVwb3J0cyBjb21lcyBmcm9tIHRoZSBbQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKV0oaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL2FjcyksIHdoaWNoIGlzIHRoZSBsYXJnZXN0IHN1cnZleSBjb25kdWN0ZWQgYnkgdGhlIFVuaXRlZCBTdGF0ZXMgQ2Vuc3VzIEJ1cmVhdS4gVGhlIHN1cnZleSBzdGFydGVkIGluIDIwMDUgYW5kIGNvbGxlY3RzIGRhdGEgZm9yIDMuNSBtaWxsaW9uIGhvdXNlaG9sZHMgYW5udWFsbHkuIERhdGEgaXMgY29sbGVjdGVkIGFib3V0IGFuY2VzdHJ5LCBjaXRpemVuc2hpcCwgaW5jb21lLCBlbXBsb3ltZW50LCBkaXNhYmlsaXR5IGFtb25nIG1hbnkgb3RoZXIgYXNwZWN0cy4gU2VlIFtoZXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BbWVyaWNhbl9Db21tdW5pdHlfU3VydmV5KSBmb3IgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3VydmV5Lg0KDQpBY2NvcmRpbmcgdG8gW1dpa2lwZWRpYV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQW1lcmljYW5fQ29tbXVuaXR5X1N1cnZleSk6DQoNCj4gRGF0YSBpcyBjb2xsZWN0ZWQgYnkgaW50ZXJuZXQsIG1haWwsIHRlbGVwaG9uZSBpbnRlcnZpZXdzIGFuZCBpbi1wZXJzb24gaW50ZXJ2aWV3cy4uLiBBYm91dCA5NSBwZXJjZW50IG9mIGhvdXNlaG9sZHMgYWNyb3NzIGFsbCByZXNwb25zZSBtb2RlcyB1bHRpbWF0ZWx5IHJlc3BvbmQuLi4gQUNTIHJlc3BvbnNlcyBhcmUgY29uZmlkZW50aWFsIg0KDQo+IEl0IGlzIGEgbWFuZGF0b3J5IHN1cnZleSwgaXQgaXMgZ292ZXJuZWQgYnkgZmVkZXJhbCBsYXdzIHRoYXQgY291bGQgaW1wb3NlIGEgZmluZSBvZiBhcyBtdWNoIGFzICQ1LDAwMCBmb3IgcmVmdXNpbmcgdG8gcGFydGljaXBhdGUuDQoNCg0KV2UgYXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluIHRoZSBmb2xsb3dpbmcgdGFibGVzIG9uIHRoZSBsYXN0IHBhZ2Ugb2YgdGhlIFtNZWFzdXJlIG9mIEFtZXJpY2EgMjAxOSByZXBvcnRdKGh0dHBzOi8vc3NyYy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS9tb2EvTWFraW5nJTIwdGhlJTIwQ29ubmVjdGlvbi5wZGYpOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBnZW5kZXJfcmFjZV9ldGhfMjAxOX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnZW5kZXJfcmFjZV9ldGhuaWNpdHlfb3ZlcnZpZXcucG5nIikpDQpgYGANCg0KQWxzbywgd2UgYXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluIHRoZSB0YWJsZXMgb24gdGhlIGZvbGxvd2luZyBwYWdlcyBmcm9tIHRoZSBbTWVhc3VyZSBvZiBBbWVyaWNhIDIwMjAgcmVwb3J0XShodHRwczovL3NzcmMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vbW9hL0FEZWNhZGVVbmRvbmUucGRmKToNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImxhdGlueF8yMDE4X292ZXJ2aWV3LnBuZyIpKQ0KYGBgDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYXNpYW5fMjAxOF9vdmVydmlldy5wbmciKSkNCmBgYA0KDQoNCg0KIyMgKipEYXRhIEltcG9ydCoqDQoqKiogDQoNCklmIHlvdSBkaWQgbm90IGNsb25lIHRoZSBHaXRIdWIgcmVwb3NpdG9yeSwgeW91IG1heSBkb3dubG9hZCB0aGUgcmF3IGRhdGEgZmlsZXMsIGluY2x1ZGluZyB0aGUgcGRmIGZpbGUgb2YgdGhlIHJlcG9ydCBhbmQgdGhlIHNjcmVlbnNob3RzLCB1c2luZyB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCiMgbGlicmFyeShPQ1NkYXRhKQ0KDQpyYXdfZGF0YSgib2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24iLCBvdXRwYXRoID0gZ2V0d2QoKSkNCiMgVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgIk9DU2RhdGEiIHN1Yi1kaXJlY3RvcnkgaW4geW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5Lg0KIyBJdCBhbHNvIGNyZWF0ZXMgYSAicmF3IiBzdWItZGlyZWN0b3J5IHdpdGhpbiAiZGF0YSIgd2hpY2ggY29udGFpbnMgdGhlIC5wZGYgZmlsZS4NCmBgYA0KDQojIyMgKipJbXBvcnRpbmcgd2l0aCBgcGRmdG9vbHNgKioNCioqKg0KDQpJZiB5b3Ugc3RpbGwgaGF2ZSB0cm91YmxlIG9idGFpbmluZyB0aGUgcGRmIGZpbGUsIHlvdSBjYW4gZG93bmxvYWQgaXQgdXNpbmcgdGhpcyBbbGlua10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi9tYXN0ZXIvZGF0YS9yYXcvTWFraW5nX3RoZV9Db25uZWN0aW9uLnBkZikuDQoNCk9uZSB3YXkgdG8gaW1wb3J0IGRhdGEgZnJvbSBhIHBkZiBpcyB0byB1c2UgdGhlIGBwZGZfdGV4dCgpYCBmdW5jdGlvbiBvZiB0aGUgYHBkZnRvb2xzYCBwYWNrYWdlLiBJbiBvdXIgY2FzZSwgd2UgZG93bmxvYWRlZCB0aGUgYE1ha2luZ190aGVfQ29ubmVjdGlvbi5wZGZgIGZpbGUgYW5kIHB1dCBpdCB3aXRoaW4gYSBgcmF3YCBzdWJkaXJlY3Rvcnkgb2YgYSBgZGF0YWAgZGlyZWN0b3J5IGZvciBvdXIgcHJvamVjdC4gSWYgeW91IHVzZSBhbiBSU3R1ZGlvIHByb2plY3QsIHRoZW4geW91IGNhbiB1c2UgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIG9mIHRoZSBgaGVyZWAgcGFja2FnZSB0byBzcGVjaWZ5IHRoZSBwYXRoIG9yIGxvY2F0aW9uIG9mIHRoZSBkb2N1bWVudCB0aGF0IHdlIHdhbnQgdG8gaW1wb3J0LiBUaGUgYGhlcmVgIHBhY2thZ2UgYXV0b21hdGljYWxseSBzdGFydHMgbG9va2luZyBmb3IgZmlsZXMgYmFzZWQgb24gd2hlcmUgeW91IGhhdmUgYSBgLlJwcm9qYCBmaWxlIHdoaWNoIGlzIGNyZWF0ZWQgd2hlbiB5b3Ugc3RhcnQgYSBuZXcgUlN0dWRpbyBwcm9qZWN0LiBXZSBjYW4gc3BlY2lmeSB0aGF0IHdlIHdhbnQgdG8gbG9vayBmb3IgdGhlIGBNYWtpbmdfdGhlX0Nvbm5lY3Rpb24ucGRmYCBmaWxlIHdpdGhpbiB0aGUgYHJhd2AgZGlyZWN0b3J5IHdpdGhpbiB0aGUgYGRhdGFgIGRpcmVjdG9yeSB3aXRoaW4gYSBkaXJlY3Rvcnkgd2hlcmUgb3VyIGAuUnByb2pgIGZpbGUgaXMgbG9jYXRlZC4gU3RhcnRpbmcgZnJvbSAiZGF0YSIsIHdlIHNlcGFyYXRlIHRoZSBuYW1lcyBvZiB0aGVzZSBkaXJlY3RvcmllcyB1c2luZyBjb21tYXMuDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KKk5vdGUgdGhlIG5leHQgY2h1bmsgb2YgY29kZSB3aWxsIG9ubHkgd29yayBmb3IgeW91IGlmIHlvdSBzZXQgdXAgeW91ciBmaWxlIHN0cnVjdHVyZSBpbiB0aGUgc2FtZSB3YXkgYXMgc3RhdGVkIGFib3ZlLioNCg0KYGBge3J9DQpwZGZfdG9vbHNfZXhhbXBsZSA8LSANCiAgcGRmdG9vbHM6OnBkZl90ZXh0KGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgIk1ha2luZ190aGVfQ29ubmVjdGlvbi5wZGYiKSkNCmBgYA0KDQpXZSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIG91dHB1dCBmb3IgdGhlIHBhZ2Ugd2l0aCBvdXIgdGFibGUgb2YgaW50ZXJlc3RzIGJ5IHNpbXBseSB1c2luZyBicmFja2V0cyBgW11gIGFyb3VuZCB0aGUgcGFnZSBudW1iZXIuIFRoZSBwYWdlIHdlIGFyZSBpbnRlcmVzdGVkIGluIChhbHRob3VnaCBsYWJlbGVkIDM5IGluIHRoZSByZXBvcnQpIGlzIHRoZSA0NHRoIHBhZ2UsIHdoaWNoIGxvb2tzIGxpa2UgdGhpczoNCg0KDQojIyMjIHsuc2Nyb2xsYWJsZSB9DQpgYGB7cn0NCiMgU2Nyb2xsIHRocm91Z2ggdGhlIG91dHB1dCENCnBkZl90b29sc19leGFtcGxlWzQ0XQ0KYGBgDQojIyMjDQoNCkZyb20gdGhlIG91dHB1dCwgaXQncyBjbGVhciB0aGF0IGEgcmVsYXRpdmVseSBsYXJnZSBhbW91bnQgb2YgbWFuaXB1bGF0aW9uIHdpbGwgYmUgcmVxdWlyZWQgdG8gd3JhbmdsZSB0aGlzIGRhdGEuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBsZWFybmluZyBtb3JlIGFib3V0IHRoZSBgcGRmdG9vbHNgIHBhY2thZ2UsIHBsZWFzZSBzZWUgdGhpcyBbY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS8pIGFuZCB0aGlzIFtjYXNlIHN0dWR5XShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vb2NzLWJwLWRpZXQvKS4NCg0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KSWYgeW91IGFyZSBmYW1pbGlhciB3aXRoIGBwZGZ0b29sc2AsIHNwZW5kIDMtNSBtaW51dGVzIHRyeWluZyB0byBleHRyYWN0IHRoZSBtdWx0aXBsZSB0YWJsZXMgZnJvbSBwYWdlIDQ0LiANCg0KIyMjIw0KDQpXaGlsZSB5b3UgbWlnaHQgZmluZCB0aGUgcXVlc3Rpb24gYWJvdmUgdG8gbm90IGJlIGltcG9zc2libGUgdXNpbmcgdGhlIGBwZGZ0b29sc2AgcGFja2FnZSwgeW91IG1pZ2h0IGZpbmQgaXQgYSBiaXQgY2hhbGxlbmdpbmcgYmVjYXVzZSBvZiBob3cgdGhlIG11bHRpcGxlIHRhYmxlcyBhcmUgZGlzcGxheWVkIG9uIHRoaXMgcGFnZS4NCg0KV2hpbGUgb3VyIG91dHB1dCBtYXkgYmUgcmVwcm9kdWNpYmxlLCB0aGlzIHByb2Nlc3MgbWF5IGJlIHRvbyB0aW1lIGNvbnN1bWluZy4NCg0KTGV0J3MgY29uc2lkZXIgYWx0ZXJuYXRpdmUgYXBwcm9hY2hlcyB0byBpbXBvcnRpbmcgdGhlc2UgZGF0YS4gDQoNCk5leHQsIHdlIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvIHByb2R1Y2UgcmVwcm9kdWNpYmxlIHRhYmxlcyB3aXRoIGltYWdlIHByb2Nlc3Npbmcgc29mdHdhcmUgaW4gYFJgIHVzaW5nIGEgcGFja2FnZSBjYWxsZWQgYG1hZ2lja2Agd2hpY2ggYWxsb3dzIGZvciB0aGUgZXh0cmFjdGlvbiBvZiB0ZXh0IGZyb20gaW1hZ2VzLiBUaGUgKiphZHZhbnRhZ2UqKiBvZiB0aGlzIG9wdGlvbiwgaXMgdGhhdCB3ZSBjYW4gdGFrZSBhIHNjcmVlbnNob3Qgb2YganVzdCBhIHBpZWNlIG9mIHRoZSBwYWdlIHRvIHdyYW5nbGUuIA0KDQoNCiMjIyAqKkltcG9ydGluZyB3aXRoIGBtYWdpY2tgKioNCioqKg0KDQpXZSB3aWxsIG5vdyBpbXBvcnQgdGhlIGRhdGEgdXNpbmcgdGhlIGBtYWdpY2tgIHBhY2thZ2Ugd2hpY2ggYWxsb3dzIGZvciB0aGUgaW1wb3J0YXRpb24gb2YgZGF0YSBmcm9tIGltYWdlcy4gDQoNCkZpcnN0LCB3ZSB3aWxsIHRha2UgYSBzY3JlZW5zaG90IChzcGVjaWZpY2FsbHkgY3JlYXRlIGEgYC5wbmdgIGZpbGUpIG9mIHRoZSB0b3AgcGFydCBvZiB0aGUgZ2VuZGVyLCByYWNlLCBhbmQgZXRobmljaXR5IHRhYmxlIG9uIHRoZSBsYXN0IHBhZ2Ugb2YgdGhlIFsyMDE5IE1lYXN1cmUgb2YgQW1lcmljYSBSZXBvcnRdKGh0dHBzOi8vc3NyYy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS9tb2EvTWFraW5nJTIwdGhlJTIwQ29ubmVjdGlvbi5wZGYpLiBXZSBhcmUgb25seSBpbnRlcmVzdGVkIGluIHRoZSBwZXJjZW50YWdlIG9mIGRpc2Nvbm5lY3Rpb24gYWNyb3NzIHRoZSB5ZWFycywgc28gd2UgZG9uJ3QgbmVlZCBvdXIgc2NyZWVuc2hvdCB0byBpbmNsdWRlIHRoZSBsYXN0IGNvdXBsZSBvZiBjb2x1bW5zLg0KDQpIZXJlLCB3ZSBzaG93IHdoYXQgdGhpcyBmaWxlIGxvb2tzIGxpa2UgaW4gdGhpcyByZW5kZXJlZCBybWFya2Rvd24gd2Vic2l0ZSBieSB1c2luZyB0aGUgYGluY2x1ZGVfZ3JhcGhpY3MoKWAgZnVuY3Rpb24gb2YgdGhlIGBrbml0cmAgcGFja2FnZS4NCg0KYGBge3IsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgIk1ham9yX2V0aG5pY19ncm91cHNfc2NyZWVuc2hvdC5wbmciKSkNCmBgYA0KDQpZb3UgY2FuIGRvd25sb2FkIHRoaXMgaW1hZ2UgZmlsZSBmcm9tIG91ciBbR2l0SHViIHJlcG9zdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24pIHRvIGZvbGxvdyBhbG9uZyBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi9tYXN0ZXIvZGF0YS9yYXcvTWFqb3JfZXRobmljX2dyb3Vwc19zY3JlZW5zaG90LnBuZykuDQoNCk5leHQsIHdlIHVzZSB0aGUgYGltYWdlX3JlYWQoKWAgZnVuY3Rpb24gb2YgdGhlIGBtYWdpY2tgIHBhY2thZ2UgdG8gaW1wb3J0IHRoaXMgaW1hZ2UuDQoNClRoZW4sIHdlIHVzZSB0aGUgYGltYWdlX2luZm8oKWAgZnVuY3Rpb24gdG8gbWFrZSBzdXJlIHRoYXQgdGhlIGltcG9ydCB3b3JrZWQgYW5kIHRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2l6ZSwgZm9ybWF0IGFuZCBjb2xvciBvZiB0aGUgaW1hZ2UuDQoNCmBgYHtyLCBkcGkgPSAxMDAwfQ0KTWFqb3JfcmFjaWFsX2V0aG5pY19ncm91cHMgPC0gDQogIG1hZ2ljazo6aW1hZ2VfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgInJhdyIsICJNYWpvcl9ldGhuaWNfZ3JvdXBzX3NjcmVlbnNob3QucG5nIikpDQoNCm1hZ2ljazo6aW1hZ2VfaW5mbyhNYWpvcl9yYWNpYWxfZXRobmljX2dyb3VwcykNCmBgYA0KDQpXZSBnZXQgc29tZSBiYXNpYyBpbmZvcm1hdGlvbiwgc3VjaCBhcyB0aGUgZmlsZSBmb3JtYXQgaXMgYSBQTkcsIHRoZSB3aWR0aCBhbmQgaGVpZ2h0IG9mIHRoZSBpbWFnZSwgZXRjLiBMZXQncyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgb3VyIGltYWdlIGluIFIhIE5vdyB0aGF0IHdlIGhhdmUgaW1wb3J0ZWQgaXQgdG8gc2VlIHRoaXMgaW1hZ2UsIHdlIHNpbXBseSBuZWVkIHRvIHR5cGUgdGhlIG5hbWUgb2YgdGhlIGltYWdlLg0KDQpgYGB7cn0NCk1ham9yX3JhY2lhbF9ldGhuaWNfZ3JvdXBzDQpgYGANCg0KTmljZSENCg0KDQpMZXQncyBpbXBvcnQgYSBjb3VwbGUgbW9yZSBpbWFnZXMganVzdCBmb3IgZnVuLiBIZXJlIHdlIHdpbGwgaW1wb3J0IGFuIGltYWdlIGRpcmVjdGx5IGZyb20gYSBVUkwuDQoNCmBgYHtyLCBvdXQud2lkdGg9IjIwJSJ9DQpnZ3Bsb3QyX3VybCA8LSAiaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC8yYzYyMzlkMzExYmU2ZDAzN2MyNTFjNzFjMzkwMjc5MmY4YzRkZGQyLzEyZjY3L2Nzcy9pbWFnZXMvaGV4L2dncGxvdDIucG5nIg0KDQpnZ3Bsb3QyX2xvZ28gPC0gaW1hZ2VfcmVhZChnZ3Bsb3QyX3VybCkNCmdncGxvdDJfbG9nbw0KYGBgDQoNCk5vdyB3ZSB3aWxsIHVzZSB0aGUgYGltYWdlX29jcigpYCBmdW5jdGlvbiBvZiB0aGUgYG1hZ2lja2AgcGFja2FnZSB0byBleHRyYWN0IHRoZSB0ZXh0IGZyb20gdGhlIGdncGxvdDIgbG9nbyBpbWFnZS4gVGhpcyBmdW5jdGlvbiB1c2VzIHRoZSBgdGVzc2VyYWN0YCBwYWNrYWdlIHdoaWNoIGhhcyB0b29scyBmb3IgW29wdGljYWwgY2hhcmFjdGVyIHJlY29nbml0aW9uIChPQ1IpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PcHRpY2FsX2NoYXJhY3Rlcl9yZWNvZ25pdGlvbiksIGhlbmNlIHRoZSBgb2NyYCBpbiB0aGUgZnVuY3Rpb24gbmFtZS4gVGhpcyBhbGxvd3MgdGhlIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IHRleHQgaW4gaW1hZ2VzLiBUaGVzZSBPQ1IgdG9vbHMgaGF2ZSBvZnRlbiBiZWVuIGRldmVsb3BlZCB1c2luZyBbbWFjaGluZSBsZWFybmluZ10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFjaGluZV9sZWFybmluZykgaW4gd2hpY2ggYW4gYWxnb3JpdGhtIHdhcyB0cmFpbmVkIG9uIGltYWdlcyB3aXRoIGFuZCB3aXRob3V0IHRleHQgdG8gImxlYXJuIiB0byByZWNvZ25pemUgdGV4dC4gU2VlIFtoZXJlXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vYS1nZW50bGUtaW50cm9kdWN0aW9uLXRvLW9jci1lZTE0NjlhMjAxYWEpIHRvIGxlYXJuIG1vcmUgYWJvdXQgaG93IE9DUiB3b3Jrcy4NCg0KYGBge3J9DQptYWdpY2s6OmltYWdlX29jcihnZ3Bsb3QyX2xvZ28pDQpgYGANCg0KQXdlc29tZSEgV2Ugd2VyZSBhYmxlIHRvIGV4dHJhY3QgdGV4dCBmcm9tIHRoaXMgaGV4IHN0aWNrZXIhDQoNCk9uZSB0aGluZyB0byBrZWVwIGluIG1pbmQgaXMgdGhhdCB0aGlzIGRvZXNuJ3QgYWx3YXlzIHdvcmsuIFVudXN1YWwgZm9udCwgYW5nbGVzIHRleHQsIG9yIHBhcnRpY3VsYXIgY29sb3JzIGNhbiBiZSBkaWZmaWN1bHQgZm9yIHRoZSBPQ1IgdG8gcmVjb2duaXplLg0KDQpIZXJlIGlzIGFuIGV4YW1wbGUgdGhhdCBkb2VzIG5vdCB3b3JrIHdpdGggdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiBgbWFnaWNrYDoNCg0KYGBge3IsIG91dC53aWR0aD0iMjAlIn0NCnRpZHl2ZXJzZV91cmwgPC0gImh0dHBzOi8vdGlkeXZlcnNlLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciDQoNCnRpZHl2ZXJzZV9sb2dvIDwtIGltYWdlX3JlYWQodGlkeXZlcnNlX3VybCkNCnRpZHl2ZXJzZV9sb2dvDQptYWdpY2s6OmltYWdlX29jcih0aWR5dmVyc2VfbG9nbykNCmBgYA0KDQpUaGlzIGlzIGxpa2VseSBkbyB0byB0aGUgYmFja2dyb3VuZCBvbiB0aGlzIHBhcnRpY3VsYXIgaGV4IHN0aWNrZXIuIFNvIHNvbWV0aW1lcyB0aGlzIHByb2Nlc3MgcmVxdWlyZXMgYSBiaXQgb2YgKip0cmlhbCBhbmQgZXJyb3IqKi4NCg0KT0shIFNvIG5vdyBsZXQncyB0cnkgZXh0cmFjdGluZyB0aGUgdGV4dCBmb3Igb3VyIGBNYWpvcl9yYWNpYWxfZXRobmljX2dyb3Vwc2AgaW1hZ2UuDQoNCiMjIyAqKk1hZ29yIFJhY2lhbCBhbmQgRXRobmljIEdyb3VwcyoqDQoNCmBgYHtyfQ0KbWFqb3JfZ3JvdXBzIDwtIG1hZ2ljazo6aW1hZ2Vfb2NyKE1ham9yX3JhY2lhbF9ldGhuaWNfZ3JvdXBzKQ0KbWFqb3JfZ3JvdXBzDQpgYGANCg0KVGhpcyBsb29rcyBsaWtlIGl0IHdvcmtlZCBmYWlybHkgd2VsbCENCg0KDQpFeGNlbGxlbnQsIG5vdyB3ZSBuZWVkIHRvIGRvIHRoZSBzYW1lIGZvciB0aGUgb3RoZXIgdHdvIHRhYmxlcyBvbiB0aGUgYm90dG9tIGhhbGYgb2YgdGhpcyBwYWdlIHRoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW46DQoNCmBgYHtyLCBlY2hvICA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImdlbmRlcl9yYWNlX2V0aG5pY2l0eV9vdmVydmlldy5wbmciKSkNCmBgYA0KDQpTbyBub3cgd2Ugd2lsbCB0YWtlIHNvbWUgc2NyZWVuc2hvdHMgb2YgdGhlIG90aGVyIHRhYmxlcyBvZiBpbnRlcmVzdC4gKipXZSB3aWxsIGxhdGVyIGV4cGxhaW4gd2h5IHdlIHRvb2sgdGhlIHNwZWNpZmljIHNjcmVlbnNob3RzIHRoYXQgd2UgZGlkLCBidXQgZm9yIG5vdyBqdXN0IGZvY3VzIG9uIGltcG9ydGluZyB0aGUgZGF0YS4qKiBJbiBnZW5lcmFsLGl0IGlzIGhlbHBmdWwgdG8gbGVhdmUgb2ZmIGFueSBhZGRpdGlvbmFsIGhlYWRlciBpbmZvcm1hdGlvbiB0aGF0IHlvdSBkb24ndCBpbnRlbmQgdG8gaW5jbHVkZSBpbiB0aGUgYWN0dWFsIHRhYmxlLiBJbiBhZGRpdGlvbiBlbXB0eSBzcGFjZXMgaW4gdGFibGVzIGNhbiBjYXVzZSB0aGUgbmVlZCBmb3IgYWRkaXRpb25hbCBpbWFnZXMuDQoNCiMjIyAqKkFzaWFuIFN1Ymdyb3VwcyAyMDE3KioNCioqKg0KRmlyc3QsIHdlIGFyZSBhbHNvIGludGVyZXN0ZWQgaW4gZGF0YSBhYm91dCB0aGUgQXNpYW4gc3ViZ3JvdXBzIGluIDIwMTcuDQoNCklmIHlvdSBoYXZlIG5vdCBkb25lIHNvIGFscmVhZHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIG5leHQgaW1hZ2UgZmlsZSBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi9tYXN0ZXIvZGF0YS9yYXcvYXNpYW5fc3ViZ3JvdXBzXzIwMTcucG5nKS4NCg0KIyMjIyB7LnJlY2FsbF9jb2RlX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkNhbiB5b3UgcmVjYWxsIHRoZSBjb21tYW5kIHRvIGltcG9ydCBhbiBpbWFnZSBpbnRvIFIgdXNpbmcgdGhlIGBtYWdpY2tgIHBhY2thZ2U/DQoNCiMjIyMNCg0KKioqDQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGNvZGUuIDwvc3VtbWFyeT4NCg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3IDwtIGltYWdlX3JlYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJyYXciLCAiYXNpYW5fc3ViZ3JvdXBzXzIwMTcucG5nIikpDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIn0NCmFzaWFuX3N1Yl8yMDE3DQpgYGANCg0KIyMjIyB7LnJlY2FsbF9jb2RlX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkNhbiB5b3UgcmVjYWxsIHRoZSBjb21tYW5kIHRvIGV4dHJhY3QgdGhlIHRleHQgZnJvbSBhbiBpbWFnZSB1c2luZyB0aGUgYG1hZ2lja2AgcGFja2FnZT8gDQoNCiMjIyMNCg0KKioqDQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGNvZGUuIDwvc3VtbWFyeT4NCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxNyA8LSBpbWFnZV9vY3IoYXNpYW5fc3ViXzIwMTcpDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxNw0KYGBgDQoNClRyaWFsIGFuZCBlcnJvciBkdXJpbmcgdGhlIHdyYW5nbGluZyBzZWN0aW9uIHdpbGwgZGVtb25zdHJhdGUgdGhhdCB3ZSBuZWVkZWQgc29tZSBhZGRpdGlvbmFsIGltYWdlcy4gV2Ugd2lsbCBpbXBvcnQgdGhlc2Ugbm93LiANCg0KSWYgeW91IGhhdmUgbm90IGRvbmUgc28gYWxyZWFkeSwgeW91IGNhbiBkb3dubG9hZCB0aGUgbmV4dCBpbWFnZSBmaWxlcyB0byBmb2xsb3cgYWxvbmcgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24vbWFzdGVyL2RhdGEvcmF3L2FzaWFuX3N1Yl8yMDE3X0EucG5nKSBhbmQgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24vbWFzdGVyL2RhdGEvcmF3L2FzaWFuX3N1Yl8yMDE3X0IucG5nKSBhbmQgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24vbWFzdGVyL2RhdGEvcmF3L2FzaWFuX3N1Yl8yMDE3X0MucG5nKS4NCg0KYGBge3IsIG91dC53aWR0aD0gIjQwJSJ9DQphc2lhbl9zdWJfMjAxN19BIDwtIGltYWdlX3JlYWQoaGVyZSgiZGF0YSIsICJyYXciLCAiYXNpYW5fc3ViXzIwMTdfQS5wbmciKSkNCmFzaWFuX3N1Yl8yMDE3X0IgPC0gaW1hZ2VfcmVhZChoZXJlKCJkYXRhIiwgInJhdyIsICJhc2lhbl9zdWJfMjAxN19CLnBuZyIpKQ0KYXNpYW5fc3ViXzIwMTdfQyA8LSBpbWFnZV9yZWFkKGhlcmUoImRhdGEiLCAicmF3IiwgImFzaWFuX3N1Yl8yMDE3X0MucG5nIikpDQoNCmFzaWFuX3N1Yl8yMDE3X0ENCmFzaWFuX3N1Yl8yMDE3X0INCmFzaWFuX3N1Yl8yMDE3X0MNCg0KYGBgDQoNCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxN19BIDwtIGltYWdlX29jcihhc2lhbl9zdWJfMjAxN19BKQ0KYXNpYW5fc3ViXzIwMTdfQiA8LSBpbWFnZV9vY3IoYXNpYW5fc3ViXzIwMTdfQikNCmFzaWFuX3N1Yl8yMDE3X0MgPC0gaW1hZ2Vfb2NyKGFzaWFuX3N1Yl8yMDE3X0MpDQoNCmFzaWFuX3N1Yl8yMDE3X0ENCmBgYA0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3X0IgDQpgYGANCg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3X0MNCmBgYA0KDQpPSywgbm93IG1vdmluZyBvbiB0byB0aGUgbGFzdCB0YWJsZSBvbiB0aGlzIHBhZ2UNCg0KIyMjICoqTGF0aW54IFN1Ymdyb3VwcyAyMDE3KioNCg0KYGBge3IsIG91dC53aWR0aCA9ICI3MCUiLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAibGF0aW54MjAxNy5wbmciKSkNCmBgYA0KDQpBZ2FpbiBhZnRlciB0cmlhbCBhbmQgZXJyb3IsIHRocmVlIHNjcmVlbnNob3RzIHdlcmUgZGV0ZXJtaW5lZCBiZXN0IGZvciBpbXBvcnRpbmcgdGhpcyBkYXRhLiANCg0KVGhlIG5hbWVzIG9mIHRoZSBmaWxlcyBmb3IgdGhlIHNjcmVlbnNob3RzIGFyZSBgbGF0aW54X3N1Yl8yMDE3X0EucG5nYCwgYCJsYXRpbnhfc3ViXzIwMTdfQi5wbmdgLCBhbmQgYCJsYXRpbnhfc3ViXzIwMTdfQy5wbmciYC4gVGhleSBhcmUgbG9jYXRlZCBpbiB0aGUgInJhdyIgc3ViZGlyZWN0b3J5IG9mIHRoZSAiZGF0YSIgZGlyZWN0b3J5IGluIG91ciBHaXRIdWIgcmVwb3NpdG9yeS4NCg0KSWYgeW91IGhhdmUgbm90IGRvbmUgc28gYWxyZWFkeSwgeW91IGNhbiBkb3dubG9hZCB0aGUgbmV4dCBpbWFnZSBmaWxlcyB0byBmb2xsb3cgYWxvbmcgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXlvdXRoLWRpc2Nvbm5lY3Rpb24vbWFzdGVyL2RhdGEvcmF3L2xhdGlueF9zdWJfMjAxN19BLnBuZykgYW5kIFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC15b3V0aC1kaXNjb25uZWN0aW9uL21hc3Rlci9kYXRhL3Jhdy9sYXRpbnhfc3ViXzIwMTdfQi5wbmcpIGFuZCBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi9tYXN0ZXIvZGF0YS9yYXcvbGF0aW54X3N1Yl8yMDE3X0MucG5nKS4NCg0KDQojIyMjIHsucmVjYWxsX2NvZGVfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KQ2FuIHlvdSByZWNhbGwgdGhlIGNvbW1hbmRzIHRvIGltcG9ydCBhbmQgZXh0cmFjdCB0aGUgZGF0YT8NCg0KIyMjIw0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgY29kZS4gPC9zdW1tYXJ5Pg0KDQpgYGB7cn0NCmxhdGlueF9pbWFnZUEgPC0gaW1hZ2VfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgInJhdyIsICJsYXRpbnhfc3ViXzIwMTdfQS5wbmciKSkNCmxhdGlueF9pbWFnZUIgPC0gaW1hZ2VfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgInJhdyIsICJsYXRpbnhfc3ViXzIwMTdfQi5wbmciKSkNCmxhdGlueF9pbWFnZUMgPC0gaW1hZ2VfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgInJhdyIsICJsYXRpbnhfc3ViXzIwMTdfQy5wbmciKSkNCg0KbGF0aW54X3N1Yl8yMDE3X0EgPC0gaW1hZ2Vfb2NyKGxhdGlueF9pbWFnZUEpDQpsYXRpbnhfc3ViXzIwMTdfQiA8LSBpbWFnZV9vY3IobGF0aW54X2ltYWdlQikNCmxhdGlueF9zdWJfMjAxN19DIDwtIGltYWdlX29jcihsYXRpbnhfaW1hZ2VDKQ0KDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIn0NCmxhdGlueF9pbWFnZUENCmxhdGlueF9zdWJfMjAxN19BDQoNCmxhdGlueF9pbWFnZUINCmxhdGlueF9zdWJfMjAxN19CDQoNCmxhdGlueF9pbWFnZUMNCmxhdGlueF9zdWJfMjAxN19DDQpgYGANCg0KDQoNCg0KR3JlYXQhIE5vdyBsZXQncyBpbXBvcnQgdGhlIHRleHQgZnJvbSBzb21lIGltYWdlcyBmb3IgdGhlIDIwMTggZGF0YSBmcm9tIHRoZSBvdGhlciBbcmVwb3J0XShodHRwczovL3NzcmMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vbW9hL0FEZWNhZGVVbmRvbmUucGRmKS4NCg0KDQojIyMgKipBc2lhbiBTdWJncm91cHMgMjAxOCoqDQoqKioNClJlY2FsbCB0aGF0IHRoaXMgd2FzIHRoZSBwYWdlIHdpdGggdGhlIHRhYmxlIG9mIGludGVyZXN0IGZvciB0aGUgQXNpYW4gc3ViZ3JvdXBzIHdpdGggMjAxOCBkYXRhOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCB9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYXNpYW5fMjAxOF9vdmVydmlldy5wbmciKSkNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlIGRhdGEgZm9yIHRoZSBzdWJncm91cHMgaXMgc2hvd24gaW4gdGhlIHRhYmxlIGJ1dCB0aGUgb3ZlcmFsbCBkYXRhIGZvciBBc2lhbnMgaXMgbG9jYXRlZCBpbiB0aGUgdGV4dC4NCg0KV2Ugd2lsbCB1c2UgYSBzY3JlZW5zaG90IG9mIGVhY2ggdG8gZXh0cmFjdCB0aGUgZGF0YSBmb3IgdGhpcyB5ZWFyLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjMwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYXNpYW5fc3ViZ3JvdXBzXzIwMTgucG5nIikpDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI4MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImFzaWFuX3lvdXRoX2V4Y2VycHQucG5nIikpDQpgYGANCg0KVHJpYWwgYW5kIGVycm9yIGluZGljYXRlZCB0aGF0IGFnYWluIGRpdmlkaW5nIHRoZSB0YWJsZSBpbnRvIG11bHRpcGxlIHNjcmVlbnNob3RzIGltcHJvdmVkIHRoZSB0ZXh0IGV4dHJhY3Rpb246DQoNCmBgYHtyfQ0KYXNpYW5fc3ViXzIwMThfQSA8LSBpbWFnZV9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgImFzaWFuX3N1Yl8yMDE4X0EucG5nIikpDQphc2lhbl9zdWJfMjAxOF9BIDwtIGltYWdlX29jcihhc2lhbl9zdWJfMjAxOF9BKQ0KYXNpYW5fc3ViXzIwMThfQiA8LSBpbWFnZV9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgImFzaWFuX3N1Yl8yMDE4X0IucG5nIikpDQphc2lhbl9zdWJfMjAxOF9CIDwtIGltYWdlX29jcihhc2lhbl9zdWJfMjAxOF9CKQ0KYGBgDQoNCg0KDQpGaW5hbGx5IGxldCdzIGdldCB0aGUgTGF0aW54IHN1Ymdyb3VwIGRhdGEgZm9yIDIwMTgNCg0KIyMjICoqTGF0aW54IFN1Ymdyb3VwcyAyMDE4KiogDQoqKioNClJlY2FsbCB0aGF0IHRoaXMgd2FzIHRoZSBwYWdlIHdpdGggdGhlIHRhYmxlIG9mIGludGVyZXN0IGZvciB0aGUgTGF0aW54IHN1Ymdyb3VwIDIwMTggZGF0YSAocHVibGlzaGVkIGluIDIwMjApOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCB9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAibGF0aW54XzIwMThfb3ZlcnZpZXcucG5nIikpDQpgYGANCg0KSW4gdGhpcyBjYXNlIG9ubHkgYSBzaW5nbGUgaW1hZ2Ugd2FzIG5lZWRlZDoNCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTggPC0gaW1hZ2VfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgInJhdyIsICJsYXRpbnhfc3ViZ3JvdXBzXzIwMTgucG5nIikpDQpsYXRpbnhfc3ViXzIwMTggPC0gaW1hZ2Vfb2NyKGxhdGlueF9zdWJfMjAxOCkNCmxhdGlueF9zdWJfMjAxOA0KYGBgDQoNCg0KRXhjZWxsZW50ISBOb3cgbGV0J3Mgc2F2ZSBhbGwgb3VyIGltcG9ydGVkIGRhdGEgZm9yIHVzZXJzIHRvIHN0YXJ0IGF0IGxhdGVyIHNlY3Rpb25zLg0KDQpgYGB7cn0NCnNhdmUoDQogIG1ham9yX2dyb3VwcywNCiAgYXNpYW5fc3ViXzIwMTcsDQogIGFzaWFuX3N1Yl8yMDE3X0EsIA0KICBhc2lhbl9zdWJfMjAxN19CLA0KICBhc2lhbl9zdWJfMjAxN19DLCANCiAgbGF0aW54X3N1Yl8yMDE3X0EsDQogIGxhdGlueF9zdWJfMjAxN19CLA0KICBsYXRpbnhfc3ViXzIwMTdfQywNCiAgYXNpYW5fc3ViXzIwMThfQSwNCiAgYXNpYW5fc3ViXzIwMThfQiwNCiAgbGF0aW54X3N1Yl8yMDE4LA0KICBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJpbXBvcnRlZCIsICJpbXBvcnRlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQojIGZvciBpbnN0cnVjdG9ycyB3aG8gd291bGQgbGlrZSB0byBoYXZlIGEgc2ltcGxlciBkYXRhIGltcG9ydCB3ZSB3aWxsIHNhdmUgY3N2IGZpbGVzOg0KDQp3cml0ZV9maWxlKG1ham9yX2dyb3VwcywgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAic2ltcGxlcl9pbXBvcnQiLCAibWFqb3JfZ3JvdXBzLmNzdiIpKQ0Kd3JpdGVfZmlsZShhc2lhbl9zdWJfMjAxNywgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAic2ltcGxlcl9pbXBvcnQiLCAiYXNpYW5fc3ViXzIwMTcuY3N2IikpDQp3cml0ZV9maWxlKGFzaWFuX3N1Yl8yMDE3X0EsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImFzaWFuX3N1Yl8yMDE3X0EuY3N2IikpDQp3cml0ZV9maWxlKGFzaWFuX3N1Yl8yMDE3X0IsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImFzaWFuX3N1Yl8yMDE3X0IuY3N2IikpDQp3cml0ZV9maWxlKGFzaWFuX3N1Yl8yMDE3X0MsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImFzaWFuX3N1Yl8yMDE3X0MuY3N2IikpDQp3cml0ZV9maWxlKGxhdGlueF9zdWJfMjAxN19BLCBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJzaW1wbGVyX2ltcG9ydCIsICJsYXRpbnhfc3ViXzIwMTdfQS5jc3YiKSkNCndyaXRlX2ZpbGUobGF0aW54X3N1Yl8yMDE3X0IsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImxhdGlueF9zdWJfMjAxN19CLmNzdiIpKQ0Kd3JpdGVfZmlsZShsYXRpbnhfc3ViXzIwMTdfQywgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAic2ltcGxlcl9pbXBvcnQiLCAibGF0aW54X3N1Yl8yMDE3X0MuY3N2IikpDQp3cml0ZV9maWxlKGFzaWFuX3N1Yl8yMDE4X0EsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImFzaWFuX3N1Yl8yMDE4X0EuY3N2IikpDQp3cml0ZV9maWxlKGFzaWFuX3N1Yl8yMDE4X0IsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgInNpbXBsZXJfaW1wb3J0IiwgImFzaWFuX3N1Yl8yMDE4X0IuY3N2IikpDQp3cml0ZV9maWxlKGxhdGlueF9zdWJfMjAxOCwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAic2ltcGxlcl9pbXBvcnQiLCAibGF0aW54X3N1Yl8yMDE4LmNzdiIpKQ0KYGBgDQoNCiMjICoqRGF0YSBXcmFuZ2xpbmcqKg0KKioqIA0KTm93IGxldCdzIHRyeSBleHRyYWN0aW5nIHRoZSB0ZXh0IGZyb20gb3VyIGltYWdlIGZpbGVzLg0KDQpJZiB5b3UgaGF2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyBidXQgc3RvcHBlZCBhbmQgYXJlIHN0YXJ0aW5nIGhlcmUgeW91IGNvdWxkIGxvYWQgdGhlIGltcG9ydGVkIGRhdGEgdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kOg0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJpbXBvcnRlZCIsICJpbXBvcnRlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBJZiB5b3Ugc2tpcHBlZCB0aGUgZGF0YSBpbXBvcnQgc2VjdGlvbiBjbGljayBoZXJlLiA8L3N1bW1hcnk+DQoNCkZpcnN0IHlvdSBuZWVkIHRvIGluc3RhbGwgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoIk9DU2RhdGEiKQ0KYGBgDQoNClRoZW4sIHlvdSBtYXkgbG9hZCB0aGUgaW1wb3J0ZWQgZGF0YSBpbnRvIHlvdXIgUiBlbnZpcm9ubWVudCB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGU6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBsaWJyYXJ5KE9DU2RhdGEpDQppbXBvcnRlZF9kYXRhKCJvY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbiIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQpJZiB0aGUgcGFja2FnZSBkb2VzIG5vdCB3b3JrIGZvciB5b3UsIGFuIFJEQSBmaWxlIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi90cmVlL21hc3Rlci9kYXRhL2ltcG9ydGVkKSBvciBzbGlnaHRseSBtb3JlIGRpcmVjdGx5IFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC15b3V0aC1kaXNjb25uZWN0aW9uL21hc3Rlci9kYXRhL2ltcG9ydGVkL2ltcG9ydGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJpbXBvcnRlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiANCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQo8L2RldGFpbHM+DQoqKioNCg0KIyMjICoqTWFqb3IgUmFjaWFsIGFuZCBFdGhuaWMgR3JvdXBzKioNCioqKg0KDQpUaGUgZmlyc3QgaW1hZ2Ugd2UgaW1wb3J0ZWQgbG9va3MgbGlrZSB0aGlzLiANCg0KYGBge3IsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgIk1ham9yX2V0aG5pY19ncm91cHNfc2NyZWVuc2hvdC5wbmciKSkNCmBgYA0KDQpBZnRlciBleHRyYWN0aW5nIHRoZSB0ZXh0IHdlIGdvdCB0aGlzOg0KDQpgYGB7cn0NCm1ham9yX2dyb3VwcyANCmBgYA0KDQpXZSBhcHBlYXIgdG8gaGF2ZSBsb3N0IHRoZSBjb2x1bW4gbmFtZXMgKGxpa2VseSBiZWNhdXNlIG9mIHRoZSBkaWZmZXJlbnQgYmFja2dyb3VuZCBjb2xvcikgYnV0IHRoZSB2YWx1ZXMgbG9vayBwcmV0dHkgZ29vZC4NCg0KWW91IG1heSBub3RpY2UgdGhhdCB0aGVyZSBhcmUgbG90cyBvZiBgXG5gIHZhbHVlcyBpbiB0aGUgdGV4dCBmcm9tIG91ciBpbWFnZS4gVGhlc2UgYXJlIFsqKm5ld2xpbmUqKl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTmV3bGluZT9vbGRmb3JtYXQ9dHJ1ZSkgY2hhcmFjdGVycywgd2hpY2ggZGVub3RlIHRoZSBlbmQgb2YgYSBsaW5lIG9mIHRleHQgYW5kIHRoZSBzdGFydCBvZiBhIG5ldyBsaW5lIG9mIHRleHQuICANCg0KVG8gZGVhbCB3aXRoIHRoZXNlLCB3ZSB1c2UgdGhlIGBzdHJfc3BsaXQoKWAgZnVuY3Rpb24gb2YgdGhlIGBzdHJpbmdyYCBwYWNrYWdlIHRvIHNwbGl0IGJhc2VkIG9uIHRoZSBgXG5gIGNoYXJhY3RlcnMgaW4gdGhlIG91dHB1dCwgd2hpY2ggdHVybnMgb3V0IHRvIGJlIGEgbGlzdC4gVGhlbiwgd2UgY2FuICJ1bmxpc3QiIHRoZSBvdXRwdXQgdXNpbmcgdGhlIGJhc2UgUiBgdW5saXN0KClgIGZ1bmN0aW9uLiBCeSBiYXNlIFIsIHdlIG1lYW4gdGhhdCB0aGUgZnVuY3Rpb24gaXQgaXMgbG9hZGVkIGF1dG9tYXRpY2FsbHkgaW4gYW4gUiBzZXNzaW9uLiBGaW5hbGx5LCB3ZSB1c2UgdGhlIGBhc190aWJibGUoKWAgZnVuY3Rpb24gb2YgdGhlIGB0aWJibGVgIHBhY2thZ2UgdG8gY29udmVydCB0aGUgZGF0YSBpbnRvIFt0aWJibGVdKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcvKSBmb3JtYXQsIHdoaWNoIGlzIHRoZSB0aWR5dmVyc2UgdmVyc2lvbiBvZiBhIGRhdGEgZnJhbWUuIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBzZWUgdGhlIHZhbHVlcyBpbiB0aGUgdGFibGUgbXVjaCBiZXR0ZXIuDQoNClRvIGRvIGFsbCBvZiB0aGVzZSBzZXF1ZW50aWFsIHN0ZXBzIGVmZmljaWVudGx5IHdlIHdpbGwgdXNlIGEgbWV0aG9kIGNhbGxlZCBwaXBpbmcuDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PkNsaWNrIGhlcmUgaWYgeW91IGFyZSB1bmZhbWlsaWFyIHdpdGggcGlwaW5nIGluIFIsIHdoaWNoIHVzZXMgdGhpcyBgJT4lYCBvcGVyYXRvci48L3N1bW1hcnk+ICANCg0KQnkgW3BpcGluZ10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ3JpdHRyL3ZpZ25ldHRlcy9tYWdyaXR0ci5odG1sKSB3ZSBtZWFuIHVzaW5nIHRoZSBgJT4lYCBwaXBlIG9wZXJhdG9yIHdoaWNoIGlzIGFjY2Vzc2libGUgYWZ0ZXIgbG9hZGluZyB0aGUgYHRpZHl2ZXJzZWAgb3Igc2V2ZXJhbCBvZiB0aGUgcGFja2FnZXMgd2l0aGluIHRoZSB0aWR5dmVyc2UgbGlrZSBgZHBseXJgIGJlY2F1c2UgdGhleSBsb2FkIHRoZSBbYG1hZ3JpdHRyYCBwYWNrYWdlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbWFncml0dHIvdmlnbmV0dGVzL21hZ3JpdHRyLmh0bWwpLiANClRoaXMgYWxsb3dzIHVzIHRvIHBlcmZvcm0gbXVsdGlwbGUgc2VxdWVudGlhbCBzdGVwcyBvbiBvbmUgZGF0YSBpbnB1dC4gIA0KSXQgdGFrZXMgdGhlIG9iamVjdCBpbiBmcm9udCBvZiB0aGUgYCU+JWAgcGlwZSBvcGVyYXRvciBhcyB0aGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIGZ1bmN0aW9uIGFmdGVyIHRoZSBwaXBlIG9wZXJhdG9yLg0KDQpGb3IgZXhhbXBsZSwgdGhlIGZvbGxvd2luZyB0d28gbGluZXMgb2YgY29kZSBhcmUgZXF1aXZhbGVudC4NCg0KYGBge3J9DQpoZWFkKG10Y2FycywgbiA9IDIpDQoNCm10Y2FycyAlPiUgaGVhZChuID0gMikNCmBgYA0KPC9kZXRhaWxzPiAgDQoqKioNCg0KYGBge3J9DQogbWFqb3JfZ3JvdXBzIDwtIA0KICBtYWpvcl9ncm91cHMgJT4lDQogIHN0cmluZ3I6OnN0cl9zcGxpdChwYXR0ZXJuID0iXG4iKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIHRpYmJsZTo6YXNfdGliYmxlKCkNCmBgYA0KDQojIyMjIHsuc2Nyb2xsYWJsZSB9DQpgYGB7cn0NCm1ham9yX2dyb3VwcyAlPiUgDQogIHByaW50KG4gPSAyMCkNCg0KYGBgDQojIyMjDQoNCg0KT0ssIHRoaXMgbG9va3MgcHJldHR5IGdvb2QhDQoNClRoZSBvbmx5IGlzc3VlIGlzIHRoYXQgc29tZSB2YWx1ZXMgYXBwZWFyIHRvIGJlIG1pc3NpbmcgYSBkZWNpbWFsIHBvaW50LiANCg0KTm8gd29ycmllcyB0aG91Z2gsIHdlIGNhbiBtb2RpZnkgdGhlIGVudGlyZSB0YWJsZSBpbiBhIHJlcHJvZHVjaWJsZSB3YXkgdG8gZ2V0IHRob3NlIGRlY2ltYWwgcGxhY2VzIGJhY2suIEhvd2V2ZXIsIGZpcnN0IHdlIG5lZWQgdG8gZG8gc29tZSBvdGhlciB3cmFuZ2xpbmcgc3RlcHMgZmlyc3QuDQoNCkZpcnN0LCBsZXQncyBzZXBhcmF0ZSB0aGUgZmlyc3QgY29sdW1uIGFib3V0IGV0aG5pY2l0aWVzIHdpdGggdGhlIHZhbHVlcyBpbiB0aGUgc3Vic2VxdWVudCBjb2x1bW5zLiBXZSBjYW4gZG8gc28gdXNpbmcgdGhlIGBzZXBhcmF0ZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIGJhc2VkIG9uIFtyZWd1bGFyIGV4cHJlc3Npb25zXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZWd1bGFyX2V4cHJlc3Npb24pLiBSZWd1bGFyIGV4cHJlc3Npb25zIChhYmJyZXZpYXRlZCByZWdleCkgYXJlIG5vdGF0aW9uIHNob3J0Y3V0cyB0aGF0IGRlc2NyaWJlIHBhdHRlcm5zIGluIGNoYXJhY3RlciBzdHJpbmdzLiBTZWUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvc3RyaW5ncy5wZGYpIGZvciBhbiBSU3R1ZGlvIGNoZWV0c2hlYXQgYWJvdXQgdGhlbS4NCg0KV2Ugd2FudCB0byBzZXBhcmF0ZSBieSBpbnN0YW5jZXMgd2hlcmUgYSBsZXR0ZXIgaXMgZm9sbG93ZWQgYnkgYSBzcGFjZSBhbmQgdGhlbiBhIG51bWJlci4gDQoNCldlIGNhbiBzcGVjaWZ5IGFueSBsZXR0ZXIgYnkgdXNpbmcgdGhlIHJlZ2V4IGBbOmFscGhhOl1gIG5vdGF0aW9uIGFuZCBhbnkgbnVtYmVyIGJ5IHVzaW5nIHRoZSByZWdleCBgWzpkaWdpdDpdYCBub3RhdGlvbi4gV2UgY291bGQgaGF2ZSBsaXN0ZWQgZXZlcnkgbGV0dGVyIHRoYXQgd2Ugc2F3IHRoZSBmaXJzdCBjb2x1bW4gZW5kaW5nIHdpdGggbGlrZSBzbyBgc3xlfEV8T3xLfE5gIGJ1dCB0aGlzIHdvdWxkIG5vdCBiZSBhcyByZXByb2R1Y2libGUgKG1lYW5pbmcgbWF5YmUgdGhpcyB3b3VsZCBub3Qgd29yayBhcyB3ZWxsIG5leHQgeWVhciBpZiBhIG5ldyBncm91cCB3ZXJlIGFkZGVkIHRoYXQgZW5kZWQgaW4gYSBkaWZmZXJlbnQgbGV0dGVyKSwgYW5kIHdlIG1pZ2h0IG1ha2UgYSBtaXN0YWtlLiBUaGlzIGlzIHdoeSB0aGUgcmVnZXggYXJlIHNvIHVzZWZ1bC4gDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0gIjQwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYWxwaGEucG5nIikpDQpgYGANCldlIGNhbiBpbmRpY2F0ZSB0aGF0IHdlIHdhbnQgYSBzcGFjZSBieSB1c2luZyB0aGlzIHJlZ2V4Og0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9ICI0MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInJlZ2V4LnBuZyIpKQ0KYGBgDQoNCg0KTm93IHRvIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIHNlZSBhIGxldHRlciBmaXJzdCBmb2xsb3dlZCBieSBhIHNwYWNlLCBmb2xsb3dlZCBieSBhIGRpZ2l0LCB3ZSBuZWVkIHRvIHVzZSBhIGxvb2sgYXJvdW5kOiANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJsb29rX2Fyb3VuZC5wbmciKSkNCmBgYA0KDQpXZSB3aWxsIHVzZSB0aGUgInByZWNlZGVkIGJ5IiBhbmQgImZvbGxvd2VkIGJ5IiBsb29rIGFyb3VuZHMuIFRodXMgYCg/PD1bWzphbHBoYTpdXSlgIHN0YW5kcyBmb3IgYW55IGxldHRlciB0aGF0IGFwcGVhcnMgYmVmb3JlIGEgc3BhY2UgYFxcc2AgdGhhdCBpcyBmb2xsb3dlZCBieSBhbnkgZGlnaXQgYCg/PVswLTldKWAuIEFsdG9nZXRoZXIgdGhlIHBhdHRlcm4gd2Ugd2FudCB0byBzZXBhcmF0ZSBieSBsb29rcyBsaWtlIHRoaXM6IGAiKD88PVtbOmFscGhhOl1dKVxccyg/PVswLTldKSJgLg0KDQoNCk5vdyB0byBzZXBhcmF0ZSB0aGUgdmFsdWUgY29sdW1uIGludG8gdHdvIGNvbHVtbnMsIHdlIGNhbiB1c2UgdGhlIGBzZXBhcmF0ZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIHRvIGRvIHRoaXMuIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBub3Qgb25seSBzcGxpdCB0aGUgcm93cyBieSBvdXIgcmVnZXggZXhwcmVzc2lvbiwgYnV0IGFsc28gdG8gY3JlYXRlIGNvbHVtbiBuYW1lcy4NCg0KVGhlcmUgYXJlIHRocmVlIGltcG9ydGFudCBhcmd1bWVudHMgZm9yIHRoZSBgc2VwYXJhdGUoKWAgZnVuY3Rpb246ICANCi0gYGNvbGAgLSB0aGlzIHNwZWNpZmllcyB3aGF0IGNvbHVtbiB5b3UgYXJlIHNlcGFyYXRpbmcgICANCi0gYGludG9gIC0gdGhpcyBzcGVjaWZpZXMgdGhlIG5hbWVzIG9mIHRoZSBuZXcgY29sdW1ucyB5b3UgYXJlIGNyZWF0aW5nICANCi0gYHNlcGAgLSB0aGlzIHNwZWNpZmllcyB3aGF0IGNoYXJhY3RlciBzdHJpbmcgdG8gbG9vayBmb3IgdG8gc2VwYXJhdGUgYnkgIA0KDQpUaHVzIHdlIHdpbGwgc2VwYXJhdGUgdGhlIGB2YWx1ZWAgY29sdW1uIGludG8gYEdyb3VwYCBhbmQgYHllYXJzYCBjb2x1bW5zLg0KDQpgYGB7cn0NCm1ham9yX2dyb3VwcyA8LSANCiAgbWFqb3JfZ3JvdXBzICU+JQ0KICB0aWR5cjo6c2VwYXJhdGUoY29sID0gdmFsdWUsIA0KICAgICAgICAgICAgICAgICAgaW50byA9IGMoIkdyb3VwIiwgIlllYXJzIiksIA0KICAgICAgICAgICAgICAgICAgc2VwID0gIig/PD1bWzphbHBoYTpdXSlcXHMoPz1bMC05XSkiKQ0KDQptYWpvcl9ncm91cHMNCmBgYA0KDQpMb29rcyBnb29kIQ0KDQpMZXQncyBhbHNvIGdldCByaWQgb2YgdGhlIGFsbCBjYXBzIGZvciB0aGUgbWFqb3IgY2F0ZWdvcmllcyBvZiB0aGUgYEdyb3VwYCBjb2x1bW4uIFdlIGNhbiBjb252ZXJ0IHRoZSB3b3JkcyB0byBvbmx5IGNhcGl0YWxpemUgdGhlIGZpcnN0IGxldHRlciB1c2luZyB0aGUgYHN0cl90b190aXRsZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuIFRvIHNwZWNpZmljYWxseSBtb2RpZnkgdGhlIGBHcm91cGAgY29sdW1uIHdlIGNhbiB1c2UgdGhlIGBtdXRhdGVgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuIA0KDQpXZSBhcmUgYWxzbyBnb2luZyB0byB1c2UgYSBzcGVjaWFsIHBpcGUgb3BlcmF0b3IgKGAlPD4lYCkgZnJvbSB0aGUgW2BtYWdyaXR0cmAgcGFja2FnZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ3JpdHRyL3ZpZ25ldHRlcy9tYWdyaXR0ci5odG1sKSBjYWxsZWQgdGhlIGNvbXBvdW5kIGFzc2lnbm1lbnQgcGlwZS1vcGVyYXRvciBvciBzb21ldGltZXMgdGhlIGRvdWJsZSBwaXBlIG9wZXJhdG9yLiANCg0KVGhpcyBhbGxvd3MgdXMgdG8gdXNlIHRoZSBgbWFqb3JfZ3JvdXBzYCBhcyBvdXIgaW5wdXQgYW5kIHJlYXNzaWduIGl0IGF0IHRoZSBlbmQgYWZ0ZXIgYWxsIHRoZSBzdWJzZXF1ZW50IHN0ZXBzIGhhdmUgYmVlbiBwZXJmb3JtZWQsIGFsdGhvdWdoIGluIHRoaXMgY2FzZSBpdCBpcyBvbmx5IG9uZSBzdGVwLg0KDQpgYGB7cn0NCm1ham9yX2dyb3VwcyAlPD4lDQogIG11dGF0ZShHcm91cCA9IHN0cmluZ3I6OnN0cl90b190aXRsZShHcm91cCkpDQoNCm1ham9yX2dyb3Vwcw0KYGBgDQoNCk5pY2UhIFRoYXQgbG9va3MgYmV0dGVyLg0KDQpGb3IgdGhlIHllYXIgZGF0YSB3ZSB3b3VsZCBsaWtlIHRvIHRyeSBzcGxpdHRpbmcgdGhlIHN0cmluZ3MgZm9yIGVhY2ggcm93IGludG8gZGlmZmVyZW50IGNvbHVtbnMgYmFzZWQgb24gYSBzcGFjZS4gQ3VycmVudGx5IGFsbCB0aGUgZGF0YSBpcyBsaXN0ZWQgaW4gb25lIGNvbHVtbiBjYWxsZWQgYFllYXJzYC4NCg0KV2UgY2FuIHVzZSB0aGUgYHNlcGFyYXRlKClgIGZ1bmN0aW9uIG9mIHRoZSBgdGlkeXJgIHBhY2thZ2UgYWdhaW4gdG8gZG8gdGhpcy4gVGhpcyB3aWxsIGFsbG93IHVzIHRvIHNwbGl0IHRoZSByb3dzIGJ5IHNwYWNlcywgYXMgd2VsbCBhcyBwcm92aWRlIG5hbWVzIGZvciB0aGUgbmV3IGNvbHVtbnMuIA0KDQoNCmBgYHtyfQ0KbWFqb3JfZ3JvdXBzICU8PiUNCiAgdGlkeXI6OnNlcGFyYXRlKGNvbCA9IFllYXJzLCANCiAgICAgICAgICAgICAgICAgIGludG8gPSBjKCIyMDA4IiwgIjIwMTAiLCAiMjAxMiIsICIyMDE0IiwgIjIwMTYiLCAiMjAxNyIpLCANCiAgICAgICAgICAgICAgICAgIHNlcCA9ICIgIikNCg0KbWFqb3JfZ3JvdXBzDQpgYGANCg0KDQoNCkxvb2tzIHByZXR0eSBnb29kIQ0KDQpXZSBhcHBlYXIgdG8gaGF2ZSBhbiBlbXB0eSByb3cgYXQgdGhlIHZlcnkgZW5kLiBTaW5jZSBhbGwgdGhlIHZhbHVlcyBhcmUgYE5BYCwgd2UgY2FuIHVzZSB0aGUgYGRyb3BfbmEoKWAgZnVuY3Rpb24gIG9mIHRoZSBgdGlkeXJgIHBhY2thZ2UgdG8gcmVtb3ZlIGl0Lg0KDQpgYGB7cn0NCm1ham9yX2dyb3VwcyAlPD4lDQogIHRpZHlyOjpkcm9wX25hKCkNCg0KbWFqb3JfZ3JvdXBzDQpgYGANCg0KR3JlYXQsIG5vdyB3ZSBoYXZlIDE4IHJvd3MuDQoNCkl0J3MgaW1wb3J0YW50IHRvIGxvb2sgdmVyeSBjYXJlZnVsbHkgYXQgdGhlIHRleHQuIEFnYWluLCB0aGVyZSBhcmUgc29tZSB2YWx1ZXMgbWlzc2luZyBhIGRlY2ltYWwgcGxhY2UuIEZvciBleGFtcGxlIHRoZSByb3cgd2hlcmUgdGhlIGBHcm91cGAgdmFsdWUgaXMgYEFzaWFuYCwgdGhlIHRoaXJkIGFuZCBmb3VydGggdmFsdWVzIGFyZSBtaXNzaW5nIGEgZGVjaW1hbCBwbGFjZS4NCg0KTG9va2luZyBhdCB0aGUgb3JpZ2luYWwgdGFibGUgd2Ugc2VlIHRoYXQgZXZlbiB2YWx1ZXMgbGlrZSAxMCBhcmUgcmVwcmVzZW50ZWQgYXMgMTAuMC4gDQoNCldlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBudW1iZXIgdmFsdWVzIGFyZSBvZiBjbGFzcyBjaGFyYWN0ZXIgYmFzZWQgb24gdGhlIGA8Y2hyPmAgYXQgdGhlIHRvcCBvZiBlYWNoIGNvbHVtbi4NCg0KKioqIA0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIGZvciBhbiBleHBsYW5hdGlvbiBhYm91dCBkYXRhIHR5cGVzIGluIFIgYW5kIGFib3V0IGNoYXJhY3RlciBzdHJpbmdzLiA8L3N1bW1hcnk+DQoNCg0KVGhlcmUgYXJlIHNldmVyYWwgY2xhc3NlcyBvZiBkYXRhIGluIFIgcHJvZ3JhbW1pbmcuIA0KQ2hhcmFjdGVyIGlzIG9uZSBvZiB0aGVzZSBjbGFzc2VzLiANCkEgY2hhcmFjdGVyIHN0cmluZyBpcyBhbiBpbmRpdmlkdWFsIGRhdGEgdmFsdWUgbWFkZSB1cCBvZiBjaGFyYWN0ZXJzLiANClRoaXMgY2FuIGJlIGEgcGFyYWdyYXBoLCBsaWtlIHRoZSBsZWdlbmQgZm9yIHRoZSB0YWJsZSwgb3IgaXQgY2FuIGJlIGEgc2luZ2xlIGxldHRlciBvciBudW1iZXIgbGlrZSB0aGUgbGV0dGVyIGAiYSJgIG9yIHRoZSBudW1iZXIgYCIzImAuIA0KSWYgZGF0YSBhcmUgb2YgY2xhc3MgY2hhcmFjdGVyLCB0aGFuIHRoZSBudW1lcmljIHZhbHVlcyB3aWxsIG5vdCBiZSBwcm9jZXNzZWQgbGlrZSBhIG51bWVyaWMgdmFsdWUgaW4gYSBtYXRoZW1hdGljYWwgc2Vuc2UuIA0KSWYgeW91IHdhbnQgeW91ciBudW1lcmljIHZhbHVlcyB0byBiZSBpbnRlcnByZXRlZCB0aGF0IHdheSwgdGhleSBuZWVkIHRvIGJlIGNvbnZlcnRlZCB0byBhIG51bWVyaWMgY2xhc3MuIA0KVGhlIG9wdGlvbnMgdHlwaWNhbGx5IHVzZWQgYXJlIGludGVnZXIgKHdoaWNoIGhhcyBubyBkZWNpbWFsIHBsYWNlKSBhbmQgZG91YmxlIHByZWNpc2lvbiAod2hpY2ggaGFzIGEgZGVjaW1hbCBwbGFjZSkuIA0KDQo8L2RldGFpbHM+DQoqKiogDQoNCg0KVG8gZml4IHRoaXMsIHdlIHdpbGwgZmlyc3QgcmVtb3ZlIGFsbCBkZWNpbWFscyBieSB0YWtpbmcgYWR2YW50YWdlIG9mIHRoZSBmYWN0IHRoYXQgdGhlc2UgdmFsdWVzIGFyZSBjaGFyYWN0ZXIgc3RyaW5ncyAodGhpcyBpcyBhbHNvIHNvcnQgb2YgbGlrZSBtdWx0aXBseWluZyBhbGwgdmFsdWVzIHRoYXQgZG8gaGF2ZSBhIGRlY2ltYWwgYnkgMTApLiBOZXh0IHdlIHdpbGwgY29udmVydCB0aGUgdmFsdWVzIHRvIGJlIG51bWVyaWMgYW5kIGZpbmFsbHkgd2Ugd2lsbCBtdWx0aXBseSBhbGwgdmFsdWVzIGJ5IC4wMSB0byBhZGQgdGhlIGRlY2ltYWxzIGJhY2suIA0KDQpIZXJlLCB3ZSB1c2UgdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24gY29tYmluZWQgd2l0aCB0aGUgYGFjcm9zcygpYCBmdW5jdGlvbiwgd2hpY2ggYWxsb3dzIHVzIHRvIHNwZWNpZnkgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBwZXJmb3JtIGEgZnVuY3Rpb24gb24uIFdlIHdhbnQgdG8gZG8gdGhpcyBmb3IgYWxsIHRoZSB5ZWFyIGNvbHVtbnMsIHNvIHdlIGNhbiBleGNsdWRlIHRoZSBgR3JvdXBgIGNvbHVtbiBieSB1c2luZyBhIG1pbnVzIHNpZ24gb3BlcmF0b3IgYC1gIGluIHRoZSBgLmNvbHNgIGFyZ3VtZW50IG9mIHRoZSBgYWNyb3NzKClgIGZ1bmN0aW9uIGxpa2Ugc286IGBtdXRhdGUoYWNyb3NzKC5jb2xzID0gLUdyb3VwKSlgDQoNCkZpbmFsbHksIHdlIHdpbGwgdXNlIHRoZSBgc3RyX3JlbW92ZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UgdG8gZmluZCBpbnN0YW5jZXMgb2YgIi4iIGFuZCByZW1vdmUgdGhlbS4gU2luY2UgIi4iIGlzIGEgcmVnZXgsIGFuZCBpbmRpY2F0ZXMgYW55IGNoYXJhY3RlciBzdHJpbmcsIHRodXMgd2UgbmVlZCAiXFwiIHRvIGhhdmUgUiBpbnRlcnByZXQgYSBkZWNpbWFsIG9yIGEgcGVyaW9kIGluc3RlYWQsIGFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgUlN0dWRpbyBjaGVhdCBzaGVldHM6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJyZWdleF9wZXJpb2QucG5nIikpDQpgYGANCg0KVG8gcGFzcyB0aGUgZGF0YSBmcm9tIGFsbCB0aGUgY29sdW1ucyBleGNlcHQgb3VyIGBHcm91cGAgdmFyaWFibGUgaW50byBvdXIgYHN0cl9yZW1vdmUoKWAgZnVuY3Rpb24sIHdlIG5lZWQgdG8gdXNlIHRoZSBgLmAgbm90YXRpb24gYXMgYSByZXBsYWNlbWVudCBmb3IgdGhlIGRhdGEgdGhhdCB3ZSBzcGVjaWZpZWQgYnkgdGhlIGAuY29sc2Bhcmd1bWVudCBhbmQgd2UgbmVlZCB0byB1c2UgYH5gIGluIGZyb250IG9mIHRoZSBmdW5jdGlvbiBuYW1lLiANCg0KYGBge3J9DQptYWpvcl9ncm91cHMgJTw+JQ0KICAgIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSAtR3JvdXAsIA0KICAgICAgICAgICAgICAgICAgfnN0cl9yZW1vdmUoc3RyaW5nID0gLiwgcGF0dGVybiA9ICJcXC4iKSkpDQoNCm1ham9yX2dyb3Vwcw0KYGBgDQoNCg0KR3JlYXQsIG5vdyBpbiBvcmRlciB0byBtdWx0aXBseSBlYWNoIHZhbHVlIGJ5IDAuMSB3ZSBuZWVkIHRvIGZpcnN0IG1ha2UgdGhlIHZhbHVlcyBudW1lcmljLiBDdXJyZW50bHkgd2UgY2FuIHRlbGwgdGhhdCB0aGV5IGFyZSBjaGFyYWN0ZXIgc3RyaW5ncyBiYXNlZCBvbiB0aGUgYDxjaGFyPmAgdmFsdWVzIGxpc3RlZCB1bmRlciBlYWNoIGNvbHVtbiBuYW1lLg0KDQoNCg0KVG8gY29udmVydCBvdXIgdmFsdWVzIHRvIGJlIG51bWVyaWMgd2UgY2FuIHVzZSB0aGUgYmFzZSBgYXMubnVtZXJpYygpYCBmdW5jdGlvbi4gQWdhaW4gd2Ugd2lsbCB1c2UgYG11dGF0ZSgpYCBhbmQgYGFjcm9zcygpYC4gU2luY2UgdGhpcyBmdW5jdGlvbiBkb2Vzbid0IHJlcXVpcmUgYW55IGFyZ3VtZW50cywgd2UgZG9uJ3QgbmVlZCB0byBzcGVjaWZ5IGl0J3MgaW5wdXQgbGlrZSB3ZSBqdXN0IGRpZCBmb3IgdGhlIGBzdHJfcmVtb3ZlKClgIGJ1dCB3ZSBjb3VsZCBkbyBzbyBhcyBzaG93biBiZWxvdy4NCg0KYGBge3J9DQptYWpvcl9ncm91cHMgJTw+JQ0KICAgbXV0YXRlKGFjcm9zcyguY29scyA9IC1Hcm91cCwgYXMubnVtZXJpYykpDQoNCiN0aGlzIGlzIGVxdWl2YWxlbnQ6DQoNCiNtYWpvcl9ncm91cHMgJTw+JQ0KIyAgIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSAtR3JvdXAsIH5hcy5udW1lcmljKC4pKSkNCg0KbWFqb3JfZ3JvdXBzDQpgYGANCg0KR3JlYXQsIHdlIGNhbiBzZWUgdGhhdCB0aGUgeWVhciB2YXJpYWJsZXMgYXJlIG5vdyBudW1lcmljIGFzIHRoZXkgYXJlIG5vdyB0eXBlIGRvdWJsZSBhcyBpbmRpY2F0ZWQgYnkgdGhlIGA8ZGJsPmAgYmVsb3cgZWFjaCBjb2x1bW4gbmFtZS4gU2VlIHRoZSBhYm92ZSBzZWN0aW9uIGFib3V0IGRhdGEgdHlwZXMgaWYgeW91IGFyZSB1bmZhbWlsaWFyIHdpdGggdHlwZSBkb3VibGUuDQoNCk9LLCBub3cgd2UgY2FuIG11bHRpcGx5IGVhY2ggdmFsdWUgYnkgMC4xIHRvIGFkZCBvdXIgZGVjaW1hbCBwb2ludHMgYmFjayBhbmQgZ2V0IGJhY2sgdG8gdGhlIG9yaWdpbmFsIHZhbHVlcy4NCg0KYGBge3J9DQoNCm1ham9yX2dyb3VwcyAlPD4lDQogICAgbXV0YXRlKGFjcm9zcyguY29scyA9IC1Hcm91cCwgfiAuICogMC4xKSkNCg0KbWFqb3JfZ3JvdXBzDQoNCmBgYA0KDQpOb3cgaXMgYSBnb29kIHRpbWUgdG8gZG91YmxlIGNoZWNrIHRoYXQgb3VyIHRhYmxlIGxvb2tzIGxpa2Ugd2hhdCB3ZSBleHBlY3QuDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJnZW5kZXJfcmFjZV9ldGhuaWNpdHkucG5nIikpDQpgYGANCg0KTG9va3MgZ29vZCENCg0KV2UgYWxzbyB3YW50IHRvIGFkZCBhIGNvdXBsZSBvZiB2YXJpYWJsZXMgYWJvdXQgYFJhY2VfRXRobmljaXR5YCBhbmQgYEdlbmRlcmAgc28gdGhhdCB3ZSBjYW4gc2VsZWN0IGFjcm9zcyBncm91cHMgbGF0ZXIuICBXZSBjYW4gdXNlIHRoZSBgcmVjb2RlKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UgdG8gY2hhbmdlIHNwZWNpZmljIHZhbHVlcywgYXMgd2UgY3JlYXRlIGEgbmV3IGBSYWNlX0V0aG5pY2l0eWAgdmFyaWFibGUgZnJvbSB0aGUgYEdyb3VwYCB2YXJpYWJsZS4gRm9yIHRoZSBEYXRhIGZvciBhbGwgb2YgdGhlIFVTIHdlIHdhbnQgdGhlIGBSYWNlX0V0aG5pY2l0eWAgdmFyaWFibGUgdmFsdWVzIHRvIGJlIGAiQWxsX3JhY2VzImAuDQoNCmBgYHtyfQ0KbWFqb3JfZ3JvdXBzICU8PiUgDQogIG11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IA0KICAgICAgICAgICByZWNvZGUoR3JvdXAsICJVbml0ZWQgU3RhdGVzIiA9ICJBbGxfcmFjZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmVtYWxlIiA9ICJBbGxfcmFjZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYWxlIiA9ICJBbGxfcmFjZXMiKSkNCg0KaGVhZChtYWpvcl9ncm91cHMpDQpgYGANCg0KDQpXZSBhbHNvIHdhbnQgdG8gcmVtb3ZlIE1hbGUgYW5kIEZlbWFsZSBmcm9tIHRoaXMgIlJhY2VfRXRobmljaXR5IiB2YXJpYWJsZSwgV2UgY2FuIGRvIHNvIHVzaW5nIHRoZSBgc3RyX3JlbW92ZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuIEltcG9ydGFudGx5LCB3ZSBhcmUgYWxzbyByZW1vdmluZyB0aGUgc3BhY2UgYmVmb3JlICJGZW1hbGUiIGFuZCAiTWFsZS4NCg0KYGBge3J9DQptYWpvcl9ncm91cHMgJTw+JSANCiAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gc3RyX3JlbW92ZShzdHJpbmcgPSBSYWNlX0V0aG5pY2l0eSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICIgRmVtYWxlfCBNYWxlIikpDQoNCmhlYWQobWFqb3JfZ3JvdXBzKQ0KYGBgDQoNCkZvciB0aGUgbmV3IGBHZW5kZXJgIHZhcmlhYmxlIHdlIHdvdWxkIGxpa2UgdG8gZXh0cmFjdCBqdXN0IHRoZSAiRmVtYWxlIiBhbmQgIk1hbGUiIHRleHQgZnJvbSB0aGUgYEdyb3VwYCB2YXJpYWJsZS4gVGhlIGBzdHJfZXh0cmFjdCgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2Ugd2lsbCBkbyB0aGlzLCBhbmQgaXQgd2lsbCBnaXZlIHVzIGFuIGBOQWAgdmFsdWUgZm9yIGFueSByb3dzIHdoZXJlICJGZW1hbGUiIG9yICJNYWxlIiB3ZXJlIG5vdCBwcmVzZW50LiBXZSBjYW4gdGhlbiByZXBsYWNlIHRoZSBgTkFgIHZhbHVlcyB3aXRoICB0aGUgdGV4dCAiQWxsIiB0byByZXByZXNlbnQgdGhlIHRvdGFsIHZhbHVlIGZvciBib3RoIG1hbGUgYW5kIGZlbWFsZSB1c2luZyB0aGUgYHJlcGxhY2VfbmEoKWAgZnVuY3Rpb24gb2YgdGhlIGB0aWR5cigpYCBwYWNrYWdlLg0KDQpgYGB7cn0NCm1ham9yX2dyb3VwcyAlPD4lIA0KICBtdXRhdGUoR2VuZGVyID0gc3RyX2V4dHJhY3Qoc3RyaW5nID0gR3JvdXAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJGZW1hbGV8TWFsZSIpKSAlPiUNCiAgbXV0YXRlKEdlbmRlciA9IHJlcGxhY2VfbmEoR2VuZGVyLCByZXBsYWNlID0gIkFsbCIpKQ0KDQpoZWFkKG1ham9yX2dyb3VwcykNCmBgYA0KDQpXZSB3b3VsZCBhbHNvIGxpa2UgdG8gcmVwbGFjZSBMYXRpbm8gYW5kIExhdGluYSB3aXRoIExhdGlueC4gDQpXZSBjYW4gIHVzZSBhbm90aGVyIGBzdHJpbmdyYCBmdW5jdGlvbiBmb3IgdGhpcy4gVGhpcyBmdW5jdGlvbiwgYHN0cl9yZXBsYWNlKClgIGFsbG93cyB1cyB0byByZW1vdmUgYW5kIHJlcGxhY2UgYSBwYXJ0aWN1bGFyIHBhdHRlcm4uIA0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KV2hhdCBvdGhlciB0aWR5dmVyc2UgZnVuY3Rpb25zIGNvdWxkIHlvdSB1c2UgdG8gcmVwbGFjZSBgTGF0aW5vYCBhbmQgYExhdGluYWAgd2l0aCBgTGF0aW54YC4gV2h5IG1pZ2h0IHRoZSBgc3RyX3JlcGxhY2UoKWAgZnVuY3Rpb24gYmUgcHJlZmVyYWJsZT8NCg0KIyMjIw0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgYW5zd2VyLiA8L3N1bW1hcnk+DQoNCldoaWxlIHRoZSBgcmVjb2RlKClgIGZ1bmN0aW9uIHdpbGwgd29yayBpbiB0aGlzIGNhc2UsIHRoZSBgc3RyX3JlcGxhY2UoKWAgZnVuY3Rpb24gY2FuIGJlIHVzZWQgbW9yZSBzaW1wbHksIGFzIGl0IHdpbGwgbG9vayBmb3IgdGhlIHBhdHRlcm4gb2YgYExhdGlub2AgYW5kIGBMYXRpbmFgIHRvIHJlcGxhY2UgZXZlbiBpZiB0aGF0IHBhdHRlcm4gaXMgcGFydCBvZiBhIHZhbHVlIHRoYXQgaGFzIG1vcmUgY2hhcmFjdGVycy4gSW4gY29udHJhc3QsIHRoZSBgcmVjb2RlYCBhbmQgYGNhc2Vfd2hlbigpYCBmdW5jdGlvbnMgd291bGQgcmVxdWlyZSB0aGF0IGBMYXRpbnhgIGJlIHNwZWNpZmllZCBmb3IgdGhlIHJlcGxhY2VtZW50IGZvciBib3RoIGBMYXRpbm9gIGFuZCBgTGF0aW5hYCBzZXBhcmF0ZWx5LCBhcyB3ZWxsIGFzIHRoZSBtb3JlIGNvbXBsaWNhdGVkIHJlcGxhY2VtZW50cyBmb3IgdGhlIGNvbXBvdW5kIGdyb3VwcyBsaWtlIGBMYXRpbnggTWFsZWAgZm9yICBgTGF0aW5vIE1hbGVgLiBUaGlzIGlzIGxvbmdlciBhbmQgdGh1cyBjcmVhdGVzIG1vcmUgb3Bwb3J0dW5pdGllcyBmb3IgYSBtaXN0YWtlLiANCg0KWW91IGNvdWxkIGFsc28gdXNlIHRoZSBgY2FzZV93aGVuKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UgaWYgeW91IGFyZSBmYW1pbGlhciB3aXRoIGl0LiBIb3dldmVyLCB0aGlzIHdvdWxkIGhhdmUgdGhlIHNhbWUgaXNzdWVzIGFzIHRoZSBgcmVjb2RlKClgIGZ1bmN0aW9uIGluIHRoYXQgaXQgcmVwbGFjZXMgdmFsdWVzIG5vdCBwYXR0ZXJucy4gSW4gYWRkaXRpb24sIHlvdSB3b3VsZCBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBvdGhlciB2YWx1ZXMgb2YgdGhlIGBSYWNlX0V0aG5pY2l0eWAgYW5kIGBHcm91cGAgdmFyaWFibGVzIHdlcmUgbm90IHJlcGxhY2VkIHdpdGggYE5BYC4NCg0KYGBge3J9DQptYWpvcl9ncm91cHMgJT4lIA0KICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSANCiAgICAgICAgICAgcmVjb2RlKFJhY2VfRXRobmljaXR5LCAiTGF0aW5vIiA9ICJMYXRpbngiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXRpbmEiID0gIkxhdGlueCIpLA0KICAgICAgICAgIEdyb3VwID0gDQogICAgICAgICAgIHJlY29kZShHcm91cCwgIkxhdGlubyIgPSAiTGF0aW54IiwNCiAgICAgICAgICAgICAgICAgICAgIkxhdGlubyBNYWxlIiA9ICJMYXRpbnggTWFsZSIsIA0KICAgICAgICAgICAgICAgICAgIkxhdGluYSBGZW1hbGUiID0gIkxhdGlueCBGZW1hbGUiKSkNCg0KYGBgDQoNCjwvZGV0YWlscz4NCg0KKioqDQoNCg0KYGBge3J9DQptYWpvcl9ncm91cHMgJTw+JQ0KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gYyhHcm91cCwgUmFjZV9FdGhuaWNpdHkpLCANCiAgICAgICAgICAgICAgICB+c3RyX3JlcGxhY2Uoc3RyaW5nID0gLiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJMYXRpbm98TGF0aW5hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIkxhdGlueCIpKSkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCm1ham9yX2dyb3Vwcw0KYGBgDQpGaW5hbGx5LCB3ZSB3b3VsZCBsaWtlIHRvIGNoYW5nZSB0aGUgc2hhcGUgb2Ygb3VyIHRhYmxlIHNvIHRoYXQgd2UgaGF2ZSBhIG5ldyBjb2x1bW4gdGhhdCByZXByZXNlbnRzIHRoZSB5ZWFyIGFuZCBhIG5ldyBjb2x1bW4gdGhhdCByZXByZXNlbnRzIHRoZSB2YWx1ZSBmb3IgdGhhdCB5ZWFyLg0KDQpUbyBkbyBzbyB3ZSB3aWxsIGJlIG1ha2luZyBvdXIgdGFibGUgImxvbmdlciIsIG1lYW5pbmcgdGhhdCBpdCB3aWxsIGhhdmUgZmV3ZXIgY29sdW1ucyBhbmQgbW9yZSByb3dzLiANClNlZSBbaGVyZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2lkZV9hbmRfbmFycm93X2RhdGEpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGRpZmZlcmVudCB0YWJsZSBmb3JtYXRzLCB0eXBpY2FsbHkgcmVmZXJyZWQgdG8gYXMgd2lkZSBhbmQgbG9uZyBvciBzb21ldGltZXMgbmFycm93Lg0KDQpXZSB3aWxsIHVzZSB0aGUgYHBpdm90X2xvbmdlcigpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIHRvIGNoYW5nZSB0aGUgc2hhcGUgb2Ygb3VyIHRhYmxlLiANCg0KVGhlcmUgYXJlIDMgbWFpbiBhcmd1bWVudHMgaW4gdGhpcyBmdW5jdGlvbjogICANCg0KMS4gYGNvbHNgIC0gc3BlY2lmaWVzIHdoYXQgY29sdW1ucyB0byBjb2xsYXBzZSAgDQoyLiBgbmFtZXNfdG9gIC0gc3BlY2lmaWVzIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1uIHRoYXQgd2lsbCBiZSBjcmVhdGVkIHRoYXQgd2lsbCBjb250YWluIHRoZSBjb2x1bW4gbmFtZXMgb2YgdGhlIGNvbHVtbnMgeW91IGFyZSBjb2xsYXBzaW5nICANCjMuIGB2YWx1ZXNfdG9gIC0gc3BlY2lmaWVzIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1uIHRoYXQgd2lsbCBiZSBjcmVhdGVkIHRoYXQgd2lsbCBjb250YWluIHRoZSB2YWx1ZXMgZnJvbSB0aGUgY29sdW1ucyB5b3UgYXJlIGNvbGxhcHNpbmcgDQoNClRvIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIGNvbGxhcHNlIGFsbCB0aGUgY29sdW1ucyB0aGF0IGhhdmUgeWVhciB2YWx1ZXMsIHdlIGNhbiBjaG9zZSB0aG9zZSB0aGF0IGNvbnRhaW4gdGhlIHN0cmluZyBgIjIwImAgdXNpbmcgdGhlIGBjb250YWlucygpYCBmdW5jdGlvbiBvZiBgZHBseXJgLiANCg0KYGBge3J9DQptYWpvcl9ncm91cHNfbG9uZyA8LSBtYWpvcl9ncm91cHMgJT4lDQogIHRpZHlyOjpwaXZvdF9sb25nZXIoY29scyA9IGNvbnRhaW5zKCIyMCIpLA0KICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiWWVhciIsDQogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50IiwNCiAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gIlBlcmNfIikgJT4lDQogIGRwbHlyOjptdXRhdGUoWWVhciA9IGFzLm51bWVyaWMoWWVhcikpDQoNCm1ham9yX2dyb3Vwc19sb25nDQpgYGANCg0KDQpFeGNlbGxlbnQsIG5vdyB3ZSBuZWVkIHRvIGRvIHRoZSBzYW1lIGZvciB0aGUgb3RoZXIgdHdvIHRhYmxlcyBvbiB0aGlzIHBhZ2U6DQoNCmBgYHtyLCBlY2hvICA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImdlbmRlcl9yYWNlX2V0aG5pY2l0eV9vdmVydmlldy5wbmciKSkNCmBgYA0KDQoNCiMjIyAqKkFzaWFuIFN1Ymdyb3VwcyAyMDE3KioNCioqKg0KDQpIZXJlIHdhcyB0aGUgdGV4dCBmcm9tIG91ciBmaXJzdCBpbWFnZSBvZiB0aGlzIHRhYmxlOg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3DQpgYGANCg0KDQpOb3cgd2UgYWdhaW4gd2FudCB0byBzcGxpdCB0aGUgZGF0YSBpbnRvIHJvd3MgYmFzZWQgb24gdGhlIG5ld2xpbmUgcmVnZXguIFRoaXMgaXMgc29tZXRoaW5nIHdlIHdpbGwgY29udGludWUgdG8gZG8gZm9yIGFsbCB0aGUgdGFibGVzLg0KDQpPbmUgb3B0aW9uIGlzIHRvIGNvcHkgYW5kIHBhc3RlIGNvZGUgd2Ugd3JvdGUgYWJvdmUgZWFjaCB0aW1lLiANCkhvd2V2ZXIsIHRoaXMgaXMgbm90IHZlcnkgZWZmaWNpZW50IGFuZCBpcyBlcnJvciBwcm9uZS4NCkFsdGVybmF0aXZlbHksIHdlIGNhbiBjcmVhdGUgYW4gUiBmdW5jdGlvbiB0byBhY2NvbXBsaXNoIHRoaXMgc3VjY2luY3RseS4gDQpGdW5jdGlvbnMgYWxsb3cgdXMgdG8gcGVyZm9ybSB0aGUgc2FtZSBwcm9jZXNzIG9uIG11bHRpcGxlIGRhdGEgaW5wdXRzLiANClNlZSBbdGhpcyBvdGhlciBjYXNlIHN0dWR5XShodHRwczovL3d3dy5vcGVuY2FzZXN0dWRpZXMub3JnL29jcy1icC12YXBpbmctY2FzZS1zdHVkeS8pIGZvciBtb3JlIGRldGFpbHMgYWJvdXQgaG93IHRvIHdyaXRlIGEgZnVuY3Rpb24uDQoNCkluIGdlbmVyYWwsIHRoZSBwcm9jZXNzIG9mIHdyaXRpbmcgZnVuY3Rpb25zIGludm9sdmVzIGZpcnN0IHNwZWNpZnlpbmcgYW4gaW5wdXQgdGhhdCBpcyB1c2VkIHdpdGhpbiB0aGUgZnVuY3Rpb24gdG8gY3JlYXRlIGFuIG91dHB1dC4gSW4gdGhpcyBjYXNlLCB0aGUgZGF0YSBpbnB1dCBpcyBgdGV4dGAgIHdoaWNoIHdpbGwgYmUgcmVwbGFjZWQgYnkgdGhlIGFjdHVhbCBpbWFnZSB0ZXh0IHRoYXQgd2UgYXJlIHdvcmtpbmcgb24sIGFuZCB0aGVuIHVzZWQgaW4gdGhlIHN1YnNlcXVlbnQgc3RlcHMgdG8gd3JhbmdsZSB0aGUgZGF0YS4gV2Ugd2lsbCBjYWxsIG91ciBmdW5jdGlvbiBgbWFrZV9yb3dzKClgLg0KDQpgYGB7cn0NCm1ha2Vfcm93cyA8LSBmdW5jdGlvbih0ZXh0KXsNCiAgdGV4dCAlPiUNCiAgc3RyX3NwbGl0KCJcbiIpICU+JQ0KICB1bmxpc3QoKSAlPiUNCiAgYXNfdGliYmxlKCkNCn0NCmBgYA0KDQpHcmVhdCEgTm93IGxldCdzIGFwcGx5IG91ciBmdW5jdGlvbiB0byB0aGUgYGFzaWFuX3N1Yl8yMDE3YCBkYXRhIQ0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3IDwtIG1ha2Vfcm93cyhhc2lhbl9zdWJfMjAxNykgDQpgYGANCg0KIyMjIyB7LnNjcm9sbGFibGUgfQ0KYGBge3J9DQojIFNjcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhDQpwcmludChhc2lhbl9zdWJfMjAxNywgbiA9IDUwKQ0KYGBgDQoNCiMjIyMNCg0KQXMgeW91IGNhbiBzZWUsIHRoZXJlIGFyZSBzb21lIHN0cmFuZ2UgdmFsdWVzIGZvciBzb21lIG9mIHRoZSByb3dzLiBGb3IgZXhhbXBsZSB0aGUgcm93IHRoYXQgc3RhcnRzIHdpdGggYENISU5FU0UgTUFsZWAgaGFzIGBBVGAgcGVyY2VudGFnZSBvZiBkaXNjb25uZWN0ZWQgeW91dGgsIGFuZCB0aGUgcm93IHRoYXQgc2hvdWxkIHNheSBgRklMSVBJTk9gIHNheXMgYFPihKJT4oSiflNTYC4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCnRlbXAgPC0gDQogIGFzaWFuX3N1Yl8yMDE3ICU+JQ0KICBwdWxsKHZhbHVlKSAlPiUNCiAgc3RyX3NwbGl0KCIgIikNCg0KdGVtcFs4XQ0KdGVtcFsyOF0NCmBgYA0KDQpOb3RpY2UgaG93IHRoZXJlIGFyZSBlbXB0eSByb3dzLiBBY2NvcmRpbmcgdG8gdGhlIHJlcG9ydCAoUERGKSwgdGhlc2Ugc3BhY2VzIGFyZSBlbXB0eSB0byBkZW5vdGUgdGhhdCB0aGUgZXN0aW1hdGVzIHdlcmUgdW5yZWxpYWJsZSBmb3IgdGhlc2UgZ3JvdXBzLiBTaW5jZSB3ZSBkb24ndCByZWFsbHkgbmVlZCB0aGVzZSByb3dzIGFzIHRoZXkgYXJlIG1pc3NpbmcgdmFsdWVzIGFueXdheSBhbmQgYmVjYXVzZSBpbWFnZXMgd2l0aCBsZXNzIHRleHQgdGhhdCBvZiBoaWdoZXIgcmVzb2x1dGlvbiBhcmUgb2Z0ZW4gZWFzaWVyIGZvciB0aGUgT0NSIHRvIGludGVycHJldCBjb3JyZWN0bHksIHdlIHdpbGwgbm93IHRyeSB1c2luZyB0aHJlZSBpbWFnZXMgb2YgdGhpcyB0YWJsZSByYXRoZXIgdGhhbiBqdXN0IG9uZS4gVGhpcyBhbGxvd3MgdXMgdG8gem9vbSBpbiBvbiB0aGUgcGFydHMgb2YgdGhlIHRhYmxlIHRoYXQgd2UgYWN0dWFsbHkgd2FudCBhbmQgaG9wZWZ1bGx5LCB0aGUgT0NSIHdpbGwgYmUgYWJsZSB0byBpbnRlcnByZXQgdGhlIHRleHQgY29ycmVjdGx5LiANCg0KU28gd2Ugd2lsbCBub3cgaW1wb3J0IGFuZCBleHRyYWN0IHRleHQgZnJvbSB0aHJlZSBzY3JlZW5zaG90cyBvZiB0aGlzIHRhYmxlIHdoZXJlIHdlIHN0b3AganVzdCBhZnRlciB0aGUgcm93IHRoYXQgc3RhcnRzIHdpdGggYFBBS0lTVEFOSWAgaW4gdGhlIGZpcnN0IGltYWdlLCBhbmQgdGhlbiBhbiBpbWFnZSBvZiB0aGUgS29yZWFuIHJvd3MgdXAgdG8gdGhlIG5leHQgcm93IHdpdGggbm8gdmFsdWVzLCBhbmQgZmluYWxseSBhbiBpbWFnZSBzdGFydGluZyBhdCB0aGUgcm93IHRoYXQgc3RhcnRzIHdpdGggYEZJTElQTk9gLiBUaHVzIGFsbCBvZiB0aGUgZW1wdHkgcm93cyB3aWxsIG5vdCBiZSBpbmNsdWRlZC4NCg0KDQpUaGVzZSBpbWFnZXMgYXJlIGNhbGxlZCBgYXNpYW5fc3ViXzIwMTdfQWAsIGBhc2lhbl9zdWJfMjAxN19CYCwgYW5kIGBhc2lhbl9zdWJfMjAxN19DYC4NCg0KTm93IHdlIHdpbGwgYXBwbHkgb3VyIGBtYWtlX3Jvd3MoKWAgZnVuY3Rpb24gdG8gdGhlIHRleHQgZnJvbSB0aGVzZSBpbWFnZXMuDQoNCmBgYHtyfQ0KYXNpYW5fc3ViXzIwMTdfQTwtIG1ha2Vfcm93cyhhc2lhbl9zdWJfMjAxN19BKQ0KYXNpYW5fc3ViXzIwMTdfQiA8LSBtYWtlX3Jvd3MoYXNpYW5fc3ViXzIwMTdfQikNCmFzaWFuX3N1Yl8yMDE3X0MgPC0gbWFrZV9yb3dzKGFzaWFuX3N1Yl8yMDE3X0MpDQoNCmFzaWFuX3N1Yl8yMDE3X0EgDQphc2lhbl9zdWJfMjAxN19CDQphc2lhbl9zdWJfMjAxN19DDQpgYGANCg0KTXVjaCBiZXR0ZXIhDQoNCldlIGNhbiBub3cgY29tYmluZSB0aGUgb2JqZWN0cyB3aXRoIHRoZSBgYmluZF9yb3dzKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UsIHdoaWNoIHdpbGwgYXBwZW5kIGVhY2ggb2YgdGhlc2UgdGliYmxlcyB0b2dldGhlciBvbmUgYWZ0ZXIgdGhlIG90aGVyLg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3IDwtYmluZF9yb3dzKGFzaWFuX3N1Yl8yMDE3X0EsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNpYW5fc3ViXzIwMTdfQiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzaWFuX3N1Yl8yMDE3X0MpDQoNCmFzaWFuX3N1Yl8yMDE3DQpgYGANCg0KTG9va3MgcHJldHR5IGdvb2QhIA0KDQpOb3cgd2UgaGF2ZSBzaW1pbGFyIHdyYW5nbGluZyBzdGVwcyB0byBwZXJmb3JtIGFzIHdlIGRpZCBwcmV2aW91c2x5IGFuZCB3ZSB3aWxsIG5lZWQgdG8gZG8gdGhlIHNhbWUgZm9yIHRoZSBMYXRpbnggc3ViZ3JvdXBzIHRhYmxlLiBTbyBpdCBpcyBhIGdvb2QgaWRlYSB0byBtYWtlIGFub3RoZXIgZnVuY3Rpb24uDQoNCkV2ZW4gdGhvdWdoIHdlIGFwcGVhciB0byBoYXZlIGFsbCBvZiB0aGUgZGVjaW1hbCBwbGFjZXMgZm9yIHRoZSB2YWx1ZXMsIHdlIHdpbGwgaW5jbHVkZSB0aGlzIGluIG91ciBmdW5jdGlvbiwganVzdCB0byBtYWtlIHN1cmUgdGhlIGRhdGEgaXMgY29ycmVjdC4NCg0KDQojIyMjIHsuZXhwbGFpbl9ibG9ja30NCjxiPjx1PiBRdWVzdGlvbiBPcHBvcnR1bml0eSA8L3U+PC9iPg0KDQpDYW4geW91IGV4cGxhaW4gd2hhdCBlYWNoIG9mIHRoZSBjb21tYW5kcyBhcmUgZG9pbmcgd2l0aGluIHRoZSBmdW5jdGlvbj8NCg0KIyMjIw0KDQpgYGB7cn0NCmNsZWFuX3RhYmxlIDwtIGZ1bmN0aW9uKHRhYmxlKXsNCiAgdGFibGUgJT4lDQogICAgc2VwYXJhdGUoLiwgY29sID0gdmFsdWUsDQogICAgICAgICAgICAgICBpbnRvID0gYygiR3JvdXAiLCAiUGVyY2VudGFnZSIpLA0KICAgICAgICAgICAgICAgIHNlcCA9ICAiKD88PVtbOmFscGhhOl1dKVxccyg/PVswLTldKSIpICU+JSANCiAgICBkcm9wX25hKCkgJT4lDQogICAgbXV0YXRlKEdyb3VwID0gc3RyX3RvX3RpdGxlKEdyb3VwKSkgJT4lDQogICAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBzdHJfcmVtb3ZlKHN0cmluZyA9IFBlcmNlbnRhZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiXFwuIikpICU+JQ0KICAgIHNlcGFyYXRlKFBlcmNlbnRhZ2UsIGMoIlBlcmNlbnQiKSwgc2VwID0gIiAiKSAlPiUNCiAgICBtdXRhdGUoUGVyY2VudCA9IGFzLm51bWVyaWMoUGVyY2VudCkpICU+JQ0KICAgIG11dGF0ZShQZXJjZW50ID0gUGVyY2VudCAqIDAuMSkgJT4lDQogICAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gcmVjb2RlKEdyb3VwLCANCiAgICAgICAgICAgICAgICAgICJVbml0ZWQgU3RhdGVzIiA9ICJBbGxfcmFjZXMiLCANCiAgICAgICAgICAgICAgICAgICJGZW1hbGUiID0gIkFsbF9yYWNlcyIsDQogICAgICAgICAgICAgICAgICAiTWFsZSIgPSAiQWxsX3JhY2VzIikpICU+JQ0KICAgIG11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IHN0cl9yZW1vdmUoc3RyaW5nID0gUmFjZV9FdGhuaWNpdHksICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiIEZlbWFsZXwgTWFsZSIpKSAlPiUNCiAgICBtdXRhdGUoR2VuZGVyID0gc3RyX2V4dHJhY3Qoc3RyaW5nID0gR3JvdXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSJGZW1hbGV8TWFsZSIpKSAlPiUNCiAgICBtdXRhdGUoR2VuZGVyID0gcmVwbGFjZV9uYShHZW5kZXIsIHJlcGxhY2UgPSAiQWxsIikpDQogDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KYXNpYW5fc3ViXzIwMTcgPC0gY2xlYW5fdGFibGUodGFibGUgPSBhc2lhbl9zdWJfMjAxNykNCmFzaWFuX3N1Yl8yMDE3DQpgYGANCg0KR3JlYXQhIFRoaXMgbG9va3MgYXMgd2UgZXhwZWN0ZWQuIA0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KV2h5IGRvIHdlIG5vdCBuZWVkIHRvIHVzZSBgcGl2b3RfbG9uZ2VyKClgIHdpdGggdGhpcyBkYXRhPw0KDQojIyMjDQoNCg0KIyMjICoqTGF0aW54IFN1Ymdyb3VwcyAyMDE3KioNCioqKg0KDQoNClJlY2FsbCB0aGF0IHRoaXMgaXMgdGhlIHRhYmxlIHdlIHdhbnQgdG8gd3JhbmdsZToNCg0KYGBge3IsIG91dC53aWR0aCA9ICI4MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImxhdGlueDIwMTcucG5nIikpDQpgYGANCg0KIyMjIyB7LnRoaW5rX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkRvIHlvdSBub3RpY2UgYW55dGhpbmcgaW5jb3JyZWN0IGFib3V0IHRoaXMgdGFibGU/IEl0J3MgYWx3YXlzIGltcG9ydGFudCB0byBiZSBwYXkgYXR0ZW50aW9uIHRvIHRoZSBkYXRhIGV2ZW4gd2hlbiBpdCBpcyBpbiByYXcgZm9ybWF0LiANCg0KIyMjIw0KDQoqKioNCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSB3aGF0IGlzIGluY29ycmVjdC4gPC9zdW1tYXJ5Pg0KDQpUaGUgbGFzdCB0d28gcm93cyBib3RoIHNheSAqKiJGZW1hbGUiKiouDQoNCjwvZGV0YWlscz4NCioqKg0KDQpTb21ldGltZXMgd2hlbiB3cmFuZ2xpbmcgdGV4dCBkYXRhLCB3ZSB3aWxsIGNvbWUgYWNyb3NzIGEgdHlwby4gV2UgbmVlZCB0byBkZXRlcm1pbmUgaG93IHRvIHJlc3BvbmQgdG8gdGhlIHR5cG8gYW5kIG1ha2Ugbm90ZSBvZiBpdC4gSXQncyBvZnRlbiBiZXN0IHRvIGNvbnN1bHQgYSBzZWNvbmRhcnkgc291cmNlIHRvIGNvbmZpcm0gdGhhdCBjaGFuZ2VzIG1hZGUgYXJlIGFjY3VyYXRlLiANCg0KRm9yIHRoZSBwdXJwb3NlcyBvZiB0aGlzIGNhc2Ugc3R1ZHksIHdlIHdpbGwgYXNzdW1lIHRoYXQgdGhlIGZpcnN0IG9mIHRoZSB0d28gcm93cyByZXByZXNlbnRzIG1hbGUgZGlzY29ubmVjdGlvbiByYXRlcyBpbiB0aGUgTGF0aW5vL2Egc3ViZ3JvdXA7IHRoaXMgd291bGQgYmUgY29uc2lzdGVudCB3aXRoIHRoZSBvcmRlcmluZyBvZiBnZW5kZXJzIGluIHRoZSBwcmV2aW91cyBzdWJncm91cHMuIA0KDQpXZSB3aWxsIG1ha2Ugc3VyZSB0byBjb3JyZWN0IHRoaXMgdHlwbyB3aGVuIHdlIGNhbi4gDQoNCkZpcnN0IGxldCdzIHRha2UgYSBsb29rIG9mIHRoZSB0ZXh0IGZyb20gdGhlIGltYWdlcyB3ZSBpbXBvcnRlZDoNCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTdfQQ0KYGBgDQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE3X0INCmBgYA0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxN19DDQpgYGANCg0KV2UgY2FuIGNvbWJpbmUgdGhlIHN0cmluZ3MgdG9nZXRoZXIgdXNpbmcgdGhlIGBzdHJfYygpYCBmdW5jdGlvbiAod2hpY2ggc3RhbmRzIGZvciBzdHJpbmcgKipjb2xsYXBzZSoqKSBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuDQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE3IDwtIHN0cmluZ3I6OnN0cl9jKGxhdGlueF9zdWJfMjAxN19BLCBsYXRpbnhfc3ViXzIwMTdfQiwgbGF0aW54X3N1Yl8yMDE3X0MpDQoNCmxhdGlueF9zdWJfMjAxNw0KYGBgDQoNCg0KTm93IGxldCdzIGNvcnJlY3QgdGhhdCB0eXBvLg0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KSG93IG1pZ2h0IHlvdSBkbyB0aGlzPw0KDQojIyMjDQoNCioqKg0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+DQoNCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTcgJTw+JSANCiAgc3RyX3JlcGxhY2Uoc3RyaW5nID0gLiwNCiAgICAgICAgICAgICBwYXR0ZXJuID0gIkRSLCBDdWJhbiBGZW1hbGUgMTUuN1xuUFIiLA0KICAgICAgICAgcmVwbGFjZW1lbnQgPSAiRFIsIEN1YmFuIE1hbGUgMTUuN1xuUFIiKQ0KYGBgDQo8L2RldGFpbHM+DQoNCioqKg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxNw0KYGBgDQoNCg0KIyMjIyB7LnJlY2FsbF9jb2RlX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkNhbiB5b3UgcmVjYWxsIHRoZSBjb21tYW5kcyB3aXRoaW4gb3VyIGBtYWtlX3Jvd3MoKWAgZnVuY3Rpb24gdG8gc2VwYXJhdGUgdGhlIGRhdGEgaW50byByb3dzIGFuZCBjcmVhdGUgYSB0aWJibGU/DQoNCiMjIyMNCg0KKioqDQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGNvZGUuIDwvc3VtbWFyeT4NCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTcgJTw+JQ0KICBzdHJfc3BsaXQoIlxuIikgJT4lDQogIHVubGlzdCgpICU+JQ0KICBhc190aWJibGUoKQ0KYGBgDQo8L2RldGFpbHM+DQoNCioqKg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxNw0KYGBgDQpOb3cgd2UgY2FuIGFwcGx5IG91ciBmdW5jdGlvbi4gDQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE3IDwtIGNsZWFuX3RhYmxlKHRhYmxlID0gbGF0aW54X3N1Yl8yMDE3KQ0KbGF0aW54X3N1Yl8yMDE3DQpgYGANCg0KDQpJdCBsb29rcyBsaWtlIHdlJ3ZlIHN1Y2Nlc3NmdWxseSBjb3JyZWN0ZWQgdGhlIHR5cG8hDQoNCg0KTGV0J3MgYWxzbyByZXBsYWNlIHRoZSBhYmJyZXZpYXRpb25zIGZvciBQdWVydG8gUmljYW4gYW5kIERvbWluaWNhbiBhbmQgbGV0J3MgcmVwbGFjZSBMYXRpbm8vTGF0aW5hIHdpdGggTGF0aW54Lg0KDQojIyMjIHsudGhpbmtfcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gUXVlc3Rpb24gT3Bwb3J0dW5pdHkgPC91PjwvYj4NCg0KSG93IG1pZ2h0IHlvdSBkbyB0aGlzIHVzaW5nIHRoZSBgc3RyX3JlcGxhY2UoKWAgZnVuY3Rpb24/DQoNCiMjIyMNCg0KKioqDQoNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGNvZGUuIDwvc3VtbWFyeT4NCg0KDQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE3ICU8PiUNCiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGMoR3JvdXAsIFJhY2VfRXRobmljaXR5KSwgDQogICAgICAgICAgIH5zdHJfcmVwbGFjZShzdHJpbmcgPSAuLA0KICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIlByLCBEciwgQ3ViYW4iLA0KICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIlB1ZXJ0byBSaWNhbiwgRG9taW5pY2FuLCBDdWJhbiIpKSwNCiAgICAgICAgIGFjcm9zcyguY29scyA9IGMoR3JvdXAsUmFjZV9FdGhuaWNpdHkpLA0KICAgICAgICAgIH5zdHJfcmVwbGFjZShzdHJpbmcgPSAuLA0KICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTGF0aW5vfExhdGluYSIsDQogICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJMYXRpbngiKSkpDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTcNCmBgYA0KDQpHcmVhdCENCg0KDQpOb3cgd2UgYXJlIHJlYWR5IHRvIGxvb2sgYXQgdGhlIGRhdGEgZnJvbSAyMDE4IGZvciB0aGUgQXNpYW4gYW5kIExhdGlueCBzdWJncm91cHMgZnJvbSB0aGUgb3RoZXIgW3JlcG9ydF0oaHR0cHM6Ly9zc3JjLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tL21vYS9BRGVjYWRlVW5kb25lLnBkZikuDQoNCg0KIyMjICoqQXNpYW4gU3ViZ3JvdXBzIDIwMTgqKg0KKioqDQpSZWNhbGwgdGhhdCB0aGlzIHdhcyB0aGUgcGFnZSB3aXRoIHRoZSB0YWJsZSBvZiBpbnRlcmVzdCBmb3IgdGhlIEFzaWFuIHN1Ymdyb3VwcyB3aXRoIDIwMTggZGF0YSAocHVibGlzaGVkIGluIDIwMjApOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCB9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYXNpYW5fMjAxOF9vdmVydmlldy5wbmciKSkNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlIGRhdGEgZm9yIHRoZSBzdWJncm91cHMgaXMgc2hvd24gaW4gdGhlIHRhYmxlIGJ1dCB0aGUgb3ZlcmFsbCBkYXRhIGZvciBBc2lhbnMgaXMgbG9jYXRlZCBpbiB0aGUgdGV4dC4NCg0KV2Ugd2lsbCB1c2UgYSBzY3JlZW5zaG90IG9mIGVhY2ggdG8gZXh0cmFjdCB0aGUgZGF0YSBmb3IgdGhpcyB5ZWFyLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjIwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiYXNpYW5fc3ViZ3JvdXBzXzIwMTgucG5nIikpDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI4MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImFzaWFuX3lvdXRoX2V4Y2VycHQucG5nIikpDQpgYGANCg0KVHJpYWwgYW5kIGVycm9yIGluZGljYXRlZCB0aGF0IGFnYWluIGRpdmlkaW5nIHRoZSB0YWJsZSBpbnRvIG11bHRpcGxlIHNjcmVlbnNob3RzIGltcHJvdmVkIHRoZSB0ZXh0IGV4dHJhY3Rpb24uIFdlIHNhdmVkIHRoZSB0ZXh0IGZyb20gdGhlc2UgaW1hZ2VzIGR1cmluZyB0aGUgWyoqRGF0YSBJbXBvcnQqKl0gc2VjdGlvbiBhcyBgYXNpYW5fc3ViXzIwMThfQWAgYW5kIGBhc2lhbl9zdWJfMjAxOF9CYC4NCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOF9BDQphc2lhbl9zdWJfMjAxOF9CDQpgYGANCg0KQWdhaW4sIGxldCBjb2xsYXBzZSB0aGVzZSB0b2dldGhlciB3aXRoIGBzdHJfYygpYCBmdW5jdGlvbi4NCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOCA8LXN0cl9jKGFzaWFuX3N1Yl8yMDE4X0EsIGFzaWFuX3N1Yl8yMDE4X0IpDQpgYGANCg0KTm93IHdlIGNhbiBhcHBseSBvdXIgYG1ha2Vfcm93cygpYCBmdW5jdGlvbjoNCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOCA8LSBtYWtlX3Jvd3MoYXNpYW5fc3ViXzIwMTgpDQoNCmFzaWFuX3N1Yl8yMDE4DQpgYGANCg0KRmlyc3QsIHdlIG5vdyBoYXZlIGNvbG9ucyBgOmAgaW4gb3VyIHRhYmxlIHRoYXQgd2Ugd2lsbCB3YW50IHRvIHNlcGFyYXRlIGJ5LiBVbmZvcnR1bmF0ZWx5LCB0aGUgdGV4dCBpbiBlYWNoIHJvdyBpc24ndCBleHRyYWN0ZWQgaW4gdGhlIHNhbWUgd2F5IGJ5IHRoZSBPQ1IuIFNvbWUgcm93cyBoYXZlIG9ubHkgYSBzcGFjZSwgd2hpbGUgb3RoZXJzIGhhdmUgc3BhY2VzIGFyb3VuZCBhIGNvbG9uOyBvciBmb3IgdGhlIHJvdyB3aXRoIGBWSUVUTkFNYCB3ZSBzZWUgYSBjb2xvbiBkaXJlY3RseSBhZnRlciB0aGUgd29yZCBmb2xsb3dlZCBieSBhIHNwYWNlLiBUaHVzLCB3ZSB3aWxsIG1vZGlmeSBvdXIgYHNlcGVyYXRlKClgIGZ1bmN0aW9uIHdpdGggdGhpcyBjaGFuZ2UuIFdlIGNhbiBzcGVjaWZ5IHRoYXQgdGhlIHNlcGFyYXRvciBiZXR3ZWVuIGFueSBsZXR0ZXIgYW5kIGFueSBkaWdpdCBzaG91bGQgYmUgZWl0aGVyIGEgc3BhY2UgKGBcXHNgKSBvciBhIGNvbG9uIHdpdGggYSBzcGFjZSBiZWZvcmUgYW5kIGFmdGVyIGl0IChgXFxzOlxcc2ApIHVzaW5nIHRoZSBvciAoYHxgKW9wZXJhdG9yLiANCg0KU28gdGhpcyB3aWxsIGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE4ICU+JQ0KICAgIHNlcGFyYXRlKC4sIGNvbCA9IHZhbHVlLA0KICAgICAgICAgICAgIGludG8gPSBjKCJHcm91cCIsICJQZXJjZW50IiksIA0KICAgICAgICAgICAgIHNlcCA9ICAiKD88PVtbOmFscGhhOl1dKVxcczpcXHN8XFxzKD89WzAtOV0pIikNCg0KYGBgDQoNClRoZW4gYmVjYXVzZSBvZiB0aGUgcm93IHdpdGggYFZJRVROQU1gLCB3ZSB3aWxsIHdhbnQgdG8gcmVtb3ZlIHRoaXMgY29sb24gdXNpbmcgdGhlIGBzdHJfcmVtb3ZlKClgIGZ1bmN0aW9uIGxpa2UgdGhpczoNCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCmFzaWFuX3N1Yl8yMDE4ICU+JQ0KICAgIHNlcGFyYXRlKC4sIGNvbCA9IHZhbHVlLCANCiAgICAgICAgICAgICAgIGludG8gPSBjKCJHcm91cCIsICJQZXJjZW50IiksIA0KICAgICAgICAgICAgICAgIHNlcCA9ICIoPzw9W1s6YWxwaGE6XV0pXFxzOlxcc3xcXHMoPz1bMC05XSkiKSAlPiUgDQogICAgbXV0YXRlKEdyb3VwPSBzdHJfcmVtb3ZlKHN0cmluZyA9IEdyb3VwLCBwYXR0ZXJuID0gIjoiKSkNCg0KYGBgDQoNClRoZSBvdGhlciBkaWZmZXJlbmNlIGZyb20gdGhlIHByZXZpb3VzIGZ1bmN0aW9uLCBpcyB0aGF0IHdlIHdhbnQgdG8gZmlsbCBpbiBhIG5ldyBgUmFjZV9FdGhuaWNpdHlgIHZhcmlhYmxlIHdpdGggdGhlIHByZXZpb3VzIHJvd3MuIFdlIGNhbiBkbyBzbyBieSBmaXJzdCByZXBsYWNpbmcgIk1lbiIgb3IgIldvbWVuIiB3aGljaCB3aXRoIHRoZSBvciBvcGVyYXRvciBpcyAoIk1lbnxXb21lbiIpLCB3aXRoICJtaXNzaW5nIi4gVGhlbiB3ZSBuZWVkIHRvIGNvbnZlcnQgdGhlc2UgdG8gYE5BYCB2YWx1ZXMgdXNpbmcgdGhlIGBuYV9pZigpYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlLCB3ZSBqdXN0IG5lZWQgdG8gc3BlY2lmeSB3aGF0IGNvbHVtbiB0byBtb2RpZnkgYW5kIHdoYXQgdmFsdWUgdG8gY2hhbmdlIHRvIGBOQWAuIEZpbmFsbHkgd2Ugd2lsbCB0aGVuIHJlcGxhY2UgdGhlIGBOQWAgdmFsdWVzIHdpdGggdGhlIHByZXZpb3VzIG5vbi1gTkFgIHZhbHVlIHVzaW5nIHRoZSBgZmlsbCgpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlLiBOb3RlIHRoYXQgdGhpcyBkb2VzIG5vdCB3b3JrIGluc2lkZSBvZiB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbi4gV2UganVzdCBuZWVkIHRvIHNpbXBseSBzcGVjaWZ5IHdoYXQgY29sdW1uIHRvIG1vZGlmeSBhbmQgdGhlbiB0aGUgZGlyZWN0aW9uIHRvIHJlcGxhY2UgdmFsdWVzLiBJbiB0aGlzIGNhc2Ugd2Ugd2FudCB0byByZXBsYWNlIGluIHRoZSBkb3dud2FyZCBkaXJlY3Rpb24gdXNpbmcgdGhlIHByZXZpb3VzIHZhbHVlcy4gDQoNClRoaXMgd2lsbCBsb29rIGxpa2UgdGhpczoNCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOCAlPiUNCnNlcGFyYXRlKC4sIGNvbCA9IHZhbHVlLCANCiAgICAgICAgICAgaW50byA9IGMoIkdyb3VwIiwgIlBlcmNlbnQiKSwgDQogICAgICAgICAgICBzZXAgPSAiKD88PVtbOmFscGhhOl1dKVxcczpcXHN8XFxzKD89WzAtOV0pIikgJT4lDQptdXRhdGUoR3JvdXAgPSBzdHJfcmVtb3ZlKHN0cmluZyA9IEdyb3VwLCBwYXR0ZXJuID0gIjoiKSkgJT4lDQptdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSBzdHJfcmVwbGFjZShzdHJpbmcgPSBHcm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJNZW58V29tZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJtaXNzaW5nIikpICU+JQ0KaGVhZCgpDQoNCmFzaWFuX3N1Yl8yMDE4ICU+JQ0Kc2VwYXJhdGUoLiwgY29sID0gdmFsdWUsIA0KICAgICAgICAgICBpbnRvID0gYygiR3JvdXAiLCAiUGVyY2VudCIpLCANCiAgICAgICAgICAgIHNlcCA9ICIoPzw9W1s6YWxwaGE6XV0pXFxzOlxcc3xcXHMoPz1bMC05XSkiKSAlPiUNCm11dGF0ZShHcm91cCA9IHN0cl9yZW1vdmUoc3RyaW5nID0gR3JvdXAsIHBhdHRlcm4gPSAiOiIpKSAlPiUNCm11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IHN0cl9yZXBsYWNlKHN0cmluZyA9IEdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIk1lbnxXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIm1pc3NpbmciKSkgJT4lDQptdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSBuYV9pZihSYWNlX0V0aG5pY2l0eSwgIm1pc3NpbmciKSkgJT4lDQpoZWFkKCkNCg0KYXNpYW5fc3ViXzIwMTggJT4lDQpzZXBhcmF0ZSguLCBjb2wgPSB2YWx1ZSwgDQogICAgICAgICAgIGludG8gPSBjKCJHcm91cCIsICJQZXJjZW50IiksIA0KICAgICAgICAgICAgc2VwID0gIig/PD1bWzphbHBoYTpdXSlcXHM6XFxzfFxccyg/PVswLTldKSIpICU+JQ0KbXV0YXRlKEdyb3VwID0gc3RyX3JlbW92ZShzdHJpbmcgPSBHcm91cCwgcGF0dGVybiA9ICI6IikpICU+JQ0KbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gR3JvdXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTWVufFdvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAibWlzc2luZyIpKSAlPiUNCm11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IG5hX2lmKFJhY2VfRXRobmljaXR5LCAibWlzc2luZyIpKSAlPiUNCmZpbGwoUmFjZV9FdGhuaWNpdHksIC5kaXJlY3Rpb24gPSAiZG93biIpICU+JQ0KaGVhZCgpDQpgYGANCg0KT0shIE5vdywgbGV0J3MgY29tYmluZSB0aGVzZSBwaWVjZXMgb2Ygb3VyIG5ldyBmdW5jdGlvbiB3aXRoIHRoZSBvbGQgcGllY2VzOg0KDQpgYGB7cn0NCg0KY2xlYW5fdGFibGUgPC0gZnVuY3Rpb24odGFibGUpew0KICB0YWJsZSAlPiUNCiAgICBzZXBhcmF0ZSguLCBjb2wgPSB2YWx1ZSwgDQogICAgICAgICAgICAgICBpbnRvID0gYygiR3JvdXAiLCAiUGVyY2VudCIpLCANCiAgICAgICAgICAgICAgICBzZXAgPSAgIig/PD1bWzphbHBoYTpdXSlcXHM6XFxzfFxccyg/PVswLTldKSIpICU+JSANCiAgICBtdXRhdGUoR3JvdXA9IHN0cl9yZW1vdmUoc3RyaW5nID0gR3JvdXAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiOiIpKSAlPiUNCiAgICBkcm9wX25hKCkgJT4lDQogICAgbXV0YXRlKEdyb3VwID0gc3RyX3RvX3RpdGxlKHN0cmluZyA9IEdyb3VwKSkgJT4lDQogICAgbXV0YXRlKFBlcmNlbnQgPSBzdHJfcmVtb3ZlKHN0cmluZyA9IFBlcmNlbnQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiXFwuIikpICU+JQ0KICAgIG11dGF0ZShQZXJjZW50ID0gYXMubnVtZXJpYyhQZXJjZW50KSkgJT4lDQogICAgbXV0YXRlKFBlcmNlbnQgPSBQZXJjZW50ICogMC4xKSAlPiUNCiAgICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSBzdHJfcmVwbGFjZShzdHJpbmcgPSBHcm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTWVufFdvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIm1pc3NpbmciKSkgJT4lDQogICAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gbmFfaWYoUmFjZV9FdGhuaWNpdHksICJtaXNzaW5nIikpICU+JQ0KICAgIGZpbGwoUmFjZV9FdGhuaWNpdHksIC5kaXJlY3Rpb24gPSAiZG93biIpICU+JQ0KICAgIG11dGF0ZShHZW5kZXIgPSBzdHJfZXh0cmFjdChzdHJpbmcgPSBHcm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJNZW58V29tZW4iKSkgJT4lDQogICAgbXV0YXRlKEdlbmRlciA9IHJlcGxhY2VfbmEoR2VuZGVyLCByZXBsYWNlID0gIkFsbCIpKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE4IDwtY2xlYW5fdGFibGUoYXNpYW5fc3ViXzIwMTgpDQphc2lhbl9zdWJfMjAxOA0KYGBgDQoNCkxvb2tpbmcgZ29vZCENCg0KDQpOb3cgd2UganVzdCBuZWVkIHRvIGFkZCB0aGUgZGF0YSBmb3IgYWxsIEFzaWFucyBmcm9tIHRoZSB0ZXh0LiANCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImFzaWFuX3lvdXRoX2V4Y2VycHQucG5nIikpDQpgYGANCg0KV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBhZGRfcm93KClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXIoKWAgcGFja2FnZS4NCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOCAlPD4lDQogIGFkZF9yb3coR3JvdXAgPSAiQXNpYW4iLA0KICAgICAgICBQZXJjZW50ID0gNi4yLA0KIFJhY2VfRXRobmljaXR5ID0gIkFzaWFuIiwNCiAgICAgICAgIEdlbmRlciA9ICJBbGwiKSAlPiUNCiAgYWRkX3JvdyhHcm91cCA9ICJBc2lhbiIsDQogICAgICAgIFBlcmNlbnQgPSA2LjQsDQogUmFjZV9FdGhuaWNpdHkgPSAiQXNpYW4iLA0KICAgICAgICAgR2VuZGVyID0gIk1lbiIpICU+JQ0KICBhZGRfcm93KEdyb3VwID0gIkFzaWFuIiwNCiAgICAgICAgUGVyY2VudCA9IDYuMSwNCiBSYWNlX0V0aG5pY2l0eSA9ICJBc2lhbiIsDQogICAgICAgICBHZW5kZXIgPSAiV29tZW4iKQ0KDQphc2lhbl9zdWJfMjAxOA0KYGBgDQoNCk9LLCBub3cgd2UganVzdCB3YW50IHRvIGNvbWJpbmUgdGhlIDIwMTggZGF0YSBhbmQgdGhlIDIwMTcgZGF0YSBmb3IgdGhlIEFzaWFuIHN1Ymdyb3Vwcy4NCg0KRmlyc3QgbGV0J3MgYWRkIGEgdmFyaWFibGUgZm9yIHllYXIgdG8gYm90aC4gVXNpbmcgYG11dGF0ZSgpYCB3ZSBjYW4gYWRkIGEgdmFyaWFibGUgYFllYXJgIHdoZXJlIGFsbCB2YWx1ZXMgYXJlIGAyMDE3YCBsaWtlIHNvOg0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE3ICU8PiUNCiAgbXV0YXRlKFllYXIgPSAyMDE3KQ0KYXNpYW5fc3ViXzIwMTcgDQpgYGANCg0KYGBge3J9DQphc2lhbl9zdWJfMjAxOCAlPD4lDQogIG11dGF0ZShZZWFyID0gMjAxOCkNCmFzaWFuX3N1Yl8yMDE4DQpgYGANCg0KWW91IG1heSBub3RpY2UgdGhhdCBgR2VuZGVyYCBpcyBjb2RlZCBkaWZmZXJlbnRseSBmb3IgdGhlIHR3byB5ZWFycy4gTGV0J3MgbWFrZSB0aGlzIGNvbnNpc3RlbnQgbm93Og0KDQpgYGB7cn0NCmFzaWFuX3N1Yl8yMDE4ICU8PiUNCiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGMoR2VuZGVyLCBHcm91cCksDQogICAgICAgICAgICAgICB+IHN0cl9yZXBsYWNlKHN0cmluZyA9IC4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJNYWxlIikpLA0KICAgICAgICAgYWNyb3NzKC5jb2xzID0gYyhHZW5kZXIsIEdyb3VwKSwNCiAgICAgICAgICAgICAgIH4gc3RyX3JlcGxhY2Uoc3RyaW5nID0gLiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiRmVtYWxlIikpKQ0KDQpgYGANCg0KV2UgY2FuIGNvbWJpbmUgdGhlc2UgdHdvIHRpYmJsZXMgdXNpbmcgdGhlIGBiaW5kX3Jvd3MoKWAgZnVuY3Rpb24gb2YgYGRwbHlyYC4gDQoNCmBgYHtyfQ0KDQphc2lhbl9zdWJncm91cHMgPC0gYmluZF9yb3dzKGFzaWFuX3N1Yl8yMDE3LCBhc2lhbl9zdWJfMjAxOCkNCmBgYA0KDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQpEVDo6ZGF0YXRhYmxlKGFzaWFuX3N1Ymdyb3VwcykNCmBgYA0KDQoNCg0KTm90aWNlIHRoYXQgdGhlcmUgYXJlIHNvbWUgY2FzZXMgd2hlcmUgd2Ugb25seSBoYXZlIG9uZSB2YWx1ZSBmb3IgYSBwYXJ0aWN1bGFyIGdyb3VwLiBGb3IgZXhhbXBsZSwgdGhlcmUgYXJlIG5vIG1hbGUgb3IgZmVtYWxlIHZhbHVlcyBmb3IgdGhlIFBha2lzdGFuaSBkYXRhLg0KDQpXZSB3b3VsZCBsaWtlIHRvIGhhdmUgYE5BYCB2YWx1ZXMgZm9yIHRoZSBjb21wYXJhYmxlIHllYXJzL2dlbmRlcnMgdGhhdCBhcmUgcG9zc2libGUuIFdlIGNhbiBmaWxsIG91dCB0aGUgcmVzdCBvZiB0aGUgdGFibGUgd2l0aCBgTkFgIHZhbHVlcyBieSBwZXJmb3JtaW5nIHRoZSBgcGl2b3Rfd2lkZXIoKWAgYW5kIGBwaXZvdF9sb25nZXIoKWAgZnVuY3Rpb25zIHNlcXVlbnRpYWxseSBsaWtlIHNvOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KYXNpYW5fc3ViZ3JvdXBzICU8PiUgDQpzZWxlY3QoLUdyb3VwKSAlPiUNCnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IFBlcmNlbnQpICU+JQ0KcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhSYWNlX0V0aG5pY2l0eSwgR2VuZGVyKSwgDQpuYW1lc190byA9IlllYXIiICwgDQp2YWx1ZXNfdG89IlBlcmNlbnQiKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCkRUOjpkYXRhdGFibGUoYXNpYW5fc3ViZ3JvdXBzKQ0KYGBgDQoNCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIGhvdyB0aGlzIGludGVyYWN0aXZlIHRhYmxlIHdhcyBjcmVhdGVkIDwvc3VtbWFyeT4NCg0KVGhpcyB0YWJsZSB3YXMgY3JlYXRlZCB1c2luZyB0aGUgYGRhdGF0YWJsZSgpYCBmdW5jdGlvbiBvZiB0aGUgYERUYCBwYWNrYWdlLiANCg0KTGlrZSBzbzoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQpEVDo6ZGF0YXRhYmxlKGFzaWFuX3N1Ymdyb3VwcykNCmBgYA0KDQo8L2RldGFpbHM+DQoqKioNCg0KR3JlYXQsIG5vdyB3ZSBhcmUgcmVhZHkgdG8gcGVyZm9ybSBzaW1pbGFyIHdyYW5nbGluZyBmb3IgdGhlIExhdGlueCBzdWJncm91cHMuDQoNCiMjIyAqKkxhdGlueCBTdWJncm91cHMgMjAxOCoqIA0KKioqDQpSZWNhbGwgdGhhdCB0aGlzIHdhcyB0aGUgcGFnZSB3aXRoIHRoZSB0YWJsZSBvZiBpbnRlcmVzdCBmb3IgdGhlIExhdGlueCBzdWJncm91cCAyMDE4IGRhdGE6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIH0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJsYXRpbnhfMjAxOF9vdmVydmlldy5wbmciKSkNCmBgYA0KDQpJbiB0aGlzIGNhc2Ugb25seSBhIHNpbmdsZSBpbWFnZSB3YXMgbmVlZGVkLiBXZSBwcmV2aW91c2x5IGltcG9ydGVkIHRoZSB0ZXh0IGZvcm0gdGhpcyBpbWFnZSBhbmQgY2FsbGVkIGl0IGBsYXRpbnhfc3ViXzIwMThgOg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxOA0KYGBgDQoNCkxldCdzIGZpcnN0IGNvbWJpbmUgdGhlIFNvdXRoIGFuZCBDZW50cmFsIEFtZXJpY2FuIGxhYmVscy4gTm90aWNlIHRoYXQgdGhlcmUgYXJlIG11bHRpcGxlIG5ldyBsaW5lIGV4cHJlc3Npb25zIGluIGJldHdlZW4gYW5kIHdlIGRvbid0IHNlZSByZXBlYXRlZCBgXG5gIGNoYXJhY3RlcnMgZWxzZXdoZXJlLg0KV2UgY2FuIHJlcGxhY2UgdGhlIHBhdHRlcm4gb2YgZXhhY3RseSB0d28gYFxuYCAodXNpbmcgYFxuezJ9YCB0byBzcGVjaWZ5IGV4YWN0bHkgMikgb3IgdHdvIG5ld2xpbmUgcmVnZXggd2l0aCBhIHNwYWNlIGFuZCBjb2xvbiBpbiBmcm9udCB3aXRoIGEgc2luZ2xlIHNwYWNlLg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxOCA8LSBzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gbGF0aW54X3N1Yl8yMDE4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIlxcczpcbnsyfXxcbnsyfSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIgIikNCmxhdGlueF9zdWJfMjAxOCANCmBgYA0KDQpOb3cgd2UgY2FuIGFwcGx5IG91ciBgbWFrZV9yb3dzKClgIGFuZCBgY2xlYW5fdGFibGUoKWAgZnVuY3Rpb25zOg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxOCA8LSBtYWtlX3Jvd3MobGF0aW54X3N1Yl8yMDE4ICkNCg0KbGF0aW54X3N1Yl8yMDE4DQpgYGANCg0KDQpgYGB7cn0NCmxhdGlueF9zdWJfMjAxOCA8LWNsZWFuX3RhYmxlKGxhdGlueF9zdWJfMjAxOCkNCg0KbGF0aW54X3N1Yl8yMDE4DQpgYGANCg0KDQpBZ2FpbiB3ZSB3aWxsIHJlcGxhY2UgYFByLCBEciwgQ3ViYW5gOg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTggJTw+JQ0KICBtdXRhdGUoR3JvdXAgPSANCiAgICAgICAgICAgc3RyX3JlcGxhY2Uoc3RyaW5nID0gR3JvdXAsDQogICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJQciwgRHIsIEN1YmFuIiwNCiAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIlB1ZXJ0byBSaWNhbiwgRG9taW5pY2FuLCBDdWJhbiIpLCANCiAgICAgICAgICBSYWNlX0V0aG5pY2l0eSA9IA0KICAgICAgICAgICBzdHJfcmVwbGFjZShzdHJpbmcgPSBSYWNlX0V0aG5pY2l0eSwNCiAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIlByLCBEciwgQ3ViYW4iLA0KICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiUHVlcnRvIFJpY2FuLCBEb21pbmljYW4sIEN1YmFuIikpDQoNCmBgYA0KDQoNCg0KV2UgYWxzbyB3YW50IHRvIGFkZCB0aGUgdG90YWwgTGF0aW54IHZhbHVlcyBhY2NvcmRpbmcgdG8gdGhlIHRleHQ6DQoNCg0KDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJsYXRpbnhfeW91dGhfZXhjZXJwdC5wbmciKSkNCg0KYGBgDQoNCg0KIyMjIyB7LnJlY2FsbF9jb2RlX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkNhbiB5b3UgcmVjYWxsIGhvdyB0byBhZGQgYWRkaXRpb25hbCByb3dzPw0KDQojIyMjDQoNCioqKg0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+DQoNCg0KYGBge3J9DQpsYXRpbnhfc3ViXzIwMTggJTw+JQ0KICBhZGRfcm93KEdyb3VwID0gIkxhdGlueCIsDQogICAgICAgIFBlcmNlbnQgPSAxMi44LA0KIFJhY2VfRXRobmljaXR5ID0gIkxhdGlueCIsDQogICAgICAgICBHZW5kZXIgPSAiQWxsIikgJT4lDQogIGFkZF9yb3coR3JvdXAgPSAiTGF0aW54IiwNCiAgICAgICAgUGVyY2VudCA9IDEyLjMsDQogUmFjZV9FdGhuaWNpdHkgPSAiTGF0aW54IiwNCiAgICAgICAgIEdlbmRlciA9ICJNZW4iKSAlPiUNCiAgYWRkX3JvdyhHcm91cCA9ICJMYXRpbngiLA0KICAgICAgICBQZXJjZW50ID0gMTMuMywNCiBSYWNlX0V0aG5pY2l0eSA9ICJMYXRpbngiLA0KICAgICAgICAgR2VuZGVyID0gIldvbWVuIikNCmBgYA0KDQo8L2RldGFpbHM+DQoNCioqKg0KDQpgYGB7cn0NCnRhaWwobGF0aW54X3N1Yl8yMDE4KQ0KYGBgDQoNCkFuZCBub3cgd2Ugd2lsbCByZWNvZGUgZ2VuZGVyIGxpa2UgYmVmb3JlIHRvIGJlIGNvbnNpc3RlbnQ6DQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE4ICU8PiUNCiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGMoR2VuZGVyLCBHcm91cCksDQogICAgICAgICAgICAgICAgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAuLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJNZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJNYWxlIikpLA0KICAgICAgICAgYWNyb3NzKC5jb2xzID0gYyhHZW5kZXIsIEdyb3VwKSwNCiAgICAgICAgICAgICAgICB+IHN0cl9yZXBsYWNlKHN0cmluZyA9IC4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiRmVtYWxlIikpKQ0KDQpoZWFkKGxhdGlueF9zdWJfMjAxOCkNCmBgYA0KDQoNCg0KTm93IHdlIGp1c3QgbmVlZCB0byBjb21iaW5lIGFsbCB0aGUgZGF0YSBmb3IgdGhlIExhdGlueCBzdWJncm91cHMuDQoNCg0KQWdhaW4sIGZpcnN0IHdlIHdpbGwgYWRkIGEgeWVhciB2YXJpYWJsZSB0byBib3RoIHRoZSAyMDE3IGFuZCAyMDE4IGRhdGEuDQoNCmBgYHtyfQ0KbGF0aW54X3N1Yl8yMDE3ICU8PiUNCiAgbXV0YXRlKFllYXIgPSAyMDE3KQ0KDQpsYXRpbnhfc3ViXzIwMTggJTw+JQ0KICBtdXRhdGUoWWVhciA9IDIwMTgpDQoNCmxhdGlueF9zdWJncm91cHMgPC0gYmluZF9yb3dzKGxhdGlueF9zdWJfMjAxNywgbGF0aW54X3N1Yl8yMDE4KQ0KbGF0aW54X3N1Ymdyb3Vwcw0KYGBgDQoNCg0KQWdhaW4sIHdlIHdvdWxkIGxpa2UgdG8gaGF2ZSBgTkFgIHZhbHVlcyBmb3IgdGhlIGNvbXBhcmFibGUgeWVhcnMvZ2VuZGVycyB0aGF0IGFyZSBwb3NzaWJsZS4gV2Ugd2lsbCBmaWxsIG91dCB0aGUgcmVzdCBvZiB0aGUgdGFibGUgd2l0aCBgTkFgIHZhbHVlcyBieSBwZXJmb3JtaW5nIHRoZSBgcGl2b3Rfd2lkZXIoKWAgYW5kIGBwaXZvdF9sb25nZXIoKWAgZnVuY3Rpb25zIHNlcXVlbnRpYWxseSBsaWtlIHNvOg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KbGF0aW54X3N1Ymdyb3VwcyAlPD4lDQogIHNlbGVjdCgtR3JvdXApICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gWWVhciwgdmFsdWVzX2Zyb20gPSBQZXJjZW50KSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhSYWNlX0V0aG5pY2l0eSwgR2VuZGVyKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9IlllYXIiICwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG89IlBlcmNlbnQiKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCkRUOjpkYXRhdGFibGUoYXNpYW5fc3ViZ3JvdXBzKQ0KYGBgDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBob3cgdGhpcyBpbnRlcmFjdGl2ZSB0YWJsZSB3YXMgY3JlYXRlZCA8L3N1bW1hcnk+DQpUaGlzIHRhYmxlIHdhcyBjcmVhdGVkIHVzaW5nIHRoZSBgZGF0YXRhYmxlKClgIGZ1bmN0aW9uIG9mIHRoZSBgRFRgIHBhY2thZ2UuIA0KDQpMaWtlIHNvOg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCkRUOjpkYXRhdGFibGUoYXNpYW5fc3ViZ3JvdXBzKQ0KYGBgDQoNCjwvZGV0YWlscz4NCioqKg0KDQoNCiMjIyAqKkNoZWNraW5nIHRoZSBkYXRhKioNCioqKg0KDQpPSywgbm93IGxldCdzIG1ha2Ugc3VyZSB0aGF0IG91ciBub3RhdGlvbnMgbWF0Y2ggYWNyb3NzIG91ciBkaWZmZXJlbnQgdGFibGVzLiBGb3IgZXhhbXBsZSBpbiB0aGUgZmlyc3QgcmVwb3J0IHRoZSB0ZXJtcyBtYWxlIGFuZCBmZW1hbGUgd2hlcmUgdXNlZCwgYnV0IGluIHRoZSBzZWNvbmQgcmVwb3J0IG1lbiBhbmQgd29tZW4gd2VyZSB1c2VkLiBMZXQncyBtYWtlIHN1cmUgZXZlcnl0aGluZyBpcyBjb25zaXN0ZW50IG5vdy4NCg0KYGBge3J9DQptYWpvcl9ncm91cHNfbG9uZw0KYXNpYW5fc3ViZ3JvdXBzDQpsYXRpbnhfc3ViZ3JvdXBzDQpgYGANCg0KTG9va3MgZ29vZCEgSWYgeW91IG1hZGUgaXQgdGhpcyBmYXIsIHlvdSBzaG91bGQgcGF0IHlvdXJzZWxmIG9uIHlvdXIgYmFjay4gVGhhdCB3YXMgYSBsb3Qgb2Ygd3JhbmdsaW5nISANCg0KTm93IGxldCdzIHNhdmUgdGhlIHdyYW5nbGVkIGRhdGEgc28gd2UgY2FuIHVzZSBpdCBmb3IgbGF0ZXIgaWYgd2Ugd2FudGVkIHRvIHN0YXJ0IGFnYWluIGF0IHRoaXMgcG9pbnQuIFdlIGNhbiBkbyBzbyB1c2luZyB0aGUgYHNhdmVgIGZ1bmN0aW9uIGxpa2Ugc28uIFdlIHdpbGwgc2F2ZSB0aGlzIGRhdGEgaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIHRoZSAiZGF0YSIgZGlyZWN0b3J5IG9mIG91ciB3b3JraW5nIGRpcmVjdG9yeS4gDQpXZSB3aWxsIGFsc28gc2F2ZSBhIGNzdiBmaWxlIGFzIHRoaXMgaXMgb2Z0ZW4gdXNlZnVsIHRvIHNlbmQgb3VyIGRhdGEgdG8gY29sbGFib3JhdG9ycy4gVG8gdXNlIHRoZSBgd3JpdGVfY3N2KClgIGZ1bmN0aW9uIHdlIG5lZWQgdG8gc2F2ZSBhIGRpZmZlcmVudCBDU1YgZmlsZSBmb3IgZWFjaCB0aWJibGUuDQoNCmBgYHtyfQ0Kc2F2ZShtYWpvcl9ncm91cHNfbG9uZywgYXNpYW5fc3ViZ3JvdXBzLCBsYXRpbnhfc3ViZ3JvdXBzLCBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsICJ3cmFuZ2xlZF9kYXRhLnJkYSIpKQ0KcmVhZHI6OndyaXRlX2NzdihtYWpvcl9ncm91cHNfbG9uZywgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfbWFqb3JfZ3JvdXBzX2RhdGEuY3N2IikpDQpyZWFkcjo6d3JpdGVfY3N2KGFzaWFuX3N1Ymdyb3VwcywgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfYXNpYW5fc3ViZ3JvdXBzX2RhdGEuY3N2IikpDQpyZWFkcjo6d3JpdGVfY3N2KGxhdGlueF9zdWJncm91cHMsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2xhdGlueF9zdWJncm91cHNfZGF0YS5jc3YiKSkNCg0KYGBgDQoNCiMjICoqRGF0YSBWaXN1YWxpemF0aW9uKioNCioqKiANCg0KUmVjYWxsIHdoYXQgb3VyIG1haW4gcXVlc3Rpb25zIHdlcmU/DQoNCg0KIyMjIyB7Lm1haW5fcXVlc3Rpb25fYmxvY2t9DQo8Yj48dT4gT3VyIG1haW4gcXVlc3Rpb25zOiA8L3U+PC9iPg0KDQoxKSBIb3cgaGF2ZSB5b3V0aCBkaXNjb25uZWN0aW9uIHJhdGVzIGluIEFtZXJpY2FuIHlvdXRoIGNoYW5nZWQgc2luY2UgMjAwOD8gICANCjIpIEluIHBhcnRpY3VsYXIsIGhvdyBoYXMgdGhpcyBjaGFuZ2VkIGZvciBkaWZmZXJlbnQgZ2VuZGVyIGFuZCBldGhuaWMgZ3JvdXBzPyBBcmUgYW55IGdyb3VwcyBwYXJ0aWN1bGFybHkgZGlzY29ubmVjdGVkPyANCg0KIyMjIw0KDQpOb3cgdGhhdCB3ZSBoYXZlIHdyYW5nbGVkIG91ciBkYXRhIGFuZCBtYWRlIGl0IGVhc3kgdG8gd29yayB3aXRoLCBsZXQncyBjcmVhdGUgc29tZSB2aXN1YWxpemF0aW9ucyB0byBleHBsb3JlIHRoZXNlIHF1ZXN0aW9ucy4NCg0KSWYgeW91IGhhdmUgYmVlbiBmb2xsb3dpbmcgYWxvbmcgYnV0IHN0b3BwZWQgYW5kIGFyZSBzdGFydGluZyBoZXJlIHlvdSBjb3VsZCBsb2FkIHRoZSB3cmFuZ2xlZCBkYXRhIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoNCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgd3JhbmdsaW5nIHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5Pg0KDQpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSBgT0NTZGF0YWAgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmBgYA0KDQpUaGVuLCB5b3UgbWF5IGxvYWQgdGhlIHdyYW5nbGVkIGRhdGEgaW50byB5b3VyIFIgZW52aXJvbm1lbnQgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgbGlicmFyeShPQ1NkYXRhKQ0Kd3JhbmdsZWRfcmRhKCJvY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbiIuIG91dHBhdGggPSBnZXR3ZCgpKQ0KbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQpJZiB0aGUgcGFja2FnZSBkb2VzIG5vdCB3b3JrIGZvciB5b3UsIGFuIFJEQSBmaWxlIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi90cmVlL21hc3Rlci9kYXRhL3dyYW5nbGVkKSBvciBzbGlnaHRseSBtb3JlIGRpcmVjdGx5IFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC15b3V0aC1kaXNjb25uZWN0aW9uL21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiANCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQo8L2RldGFpbHM+DQoqKioNCg0KDQojIyMgKipNYWpvciBSYWNlIGFuZCBFdGhuaWMgR3JvdXBzIFBsb3QqKg0KKioqDQoNCldlIGFyZSBwYXJ0aWN1bGFybHkgaW50ZXJlc3RlZCBpbiBiZWluZyBhYmxlIHRvIHJlcHJvZHVjZSB0aGUgcGxvdCBiZWxvdyBmcm9tIHRoZSByZXBvcnQsIGFzIHdlIHdvdWxkIGxpa2UgdG8gbWFrZSBzaW1pbGFyIGxvb2tpbmcgcGxvdHMuDQoNCmBgYHtyLCBvdXQud2lkdGggPSAiMTAwJSIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0NCmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk1ha2luZ190aGVfQ29ubmVjdGlvbl9wbG90LnBuZyIpKQ0KYGBgDQoNCkluIGdlbmVyYWwsIGl0IGlzIHZlcnkgdXNlZnVsIHRvIGxlYXJuIGhvdyB0byByZXByb2R1Y2UgdGhlIHN0eWxlIG9mIGEgcGxvdC4NCg0KDQpUaGVyZSBhcmUgY29sb3IgaWRlbnRpZnlpbmcgd2Vic2l0ZXMgc3VjaCBhcyBbdGhpc10oaHR0cHM6Ly9pbWFnZWNvbG9ycGlja2VyLmNvbS9lbi8pLg0KDQpVc2luZyBvbmUgb2YgdGhlc2Ugd2Vic2l0ZXMsIHdlIGlkZW50aWZ5IHRoZSBbaGV4IHRyaXBsZXRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9jb2xvcnM/b2xkZm9ybWF0PXRydWUpIGNvZGUgZm9yIHRoZSBjb2xvciB1c2VkIGluIHRoZSB2aXN1YWxpemF0aW9uIGluY2x1ZGVkIGluIHRoZSBQREYgOiBgIzAwODM5M2AuICBUaHVzIHdlIHdpbGwgdXNlIHRoaXMgY29sb3Igb3VyIHBsb3QuDQoNCg0KV2UgY2FuIGNyZWF0ZSBhIHZlcnNpb24gb2YgdGhlIGFib3ZlIHBsb3QgdXNpbmcgdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIG9mIHRoZSBfdGlkeXZlcnNlXyB0byBjcmVhdGUgb3VyIHBsb3RzLg0KDQoqKioNCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayBoZXJlIGZvciBhbiBpbnRyb2R1Y3Rpb24gYWJvdXQgdGhpcyBwYWNrYWdlIGlmIHlvdSBhcmUgIG5ldyB0byB1c2luZyBgZ2dwbG90MmAgPC9zdW1tYXJ5Pg0KDQoqKioNClRoZSBbZ2dwbG90MiBwYWNrYWdlXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnKSBpcyBnZW5lcmFsbHkgaW50dWl0aXZlIGZvciBiZWdpbm5lcnMgYmVjYXVzZSBpdCBpcyBiYXNlZCBvbiBhICBbZ3JhbW1hciBvZiBncmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIuaHRtbCkgb3IgdGhlIGBnZ2AgaW4gYGdncGxvdDJgLiANClRoZSBpZGVhIGlzIHRoYXQgeW91IGNhbiBjb25zdHJ1Y3QgbWFueSBzZW50ZW5jZXMgYnkgbGVhcm5pbmcganVzdCBhIGZldyBub3VucywgYWRqZWN0aXZlcywgYW5kIHZlcmJzLiBUaGVyZSBhcmUgc3BlY2lmaWMg4oCcd29yZHPigJ0gdGhhdCB3ZSB3aWxsIG5lZWQgdG8gbGVhcm4gYW5kIG9uY2Ugd2UgZG8sIHlvdSB3aWxsIGJlIGFibGUgdG8gY3JlYXRlIChvciDigJx3cml0ZeKAnSkgaHVuZHJlZHMgb2YgZGlmZmVyZW50IHBsb3RzLg0KDQpUaGUgY3JpdGljYWwgcGFydCB0byBtYWtpbmcgZ3JhcGhpY3MgdXNpbmcgYGdncGxvdDJgIGlzIHRoZSBkYXRhIG5lZWRzIHRvIGJlIGluIGEgX3RpZHlfIGZvcm1hdC4gDQpHaXZlbiB0aGF0IHdlIGhhdmUganVzdCBzcGVudCB0aW1lIHB1dHRpbmcgb3VyIGRhdGEgaW4gX3RpZHlfIGZvcm1hdCwgd2UgYXJlIHByaW1lZCB0byB0YWtlIGFkdmFudGFnZSBvZiBhbGwgdGhhdCBgZ2dwbG90MmAgaGFzIHRvIG9mZmVyISANCg0KV2Ugd2lsbCBzaG93IGhvdyBpdCBpcyBlYXN5IHRvIHBpcGUgX3RpZHlfIGRhdGEgKG91dHB1dCkgYXMgaW5wdXQgdG8gb3RoZXIgZnVuY3Rpb25zIHRoYXQgY3JlYXRlIHBsb3RzLiANClRoaXMgYWxsIHdvcmtzIGJlY2F1c2Ugd2UgYXJlIHdvcmtpbmcgDQp3aXRoaW4gdGhlIF90aWR5dmVyc2VfLiANCg0KKipXaGF0IGlzIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uPyoqIA0KQXMgZXhwbGFpbmVkIGJ5IEhhZGxleSBXaWNraGFtOg0KDQo+IFRoZSBncmFtbWFyIHRlbGxzIHVzIHRoYXQgYSBzdGF0aXN0aWNhbCBncmFwaGljIGlzIGEgbWFwcGluZyBmcm9tIGRhdGEgdG8gYWVzdGhldGljIGF0dHJpYnV0ZXMgKGNvbG91ciwgc2hhcGUsIHNpemUpIG9mIGdlb21ldHJpYyBvYmplY3RzIChwb2ludHMsIGxpbmVzLCBiYXJzKS4gVGhlIHBsb3QgbWF5IGFsc28gY29udGFpbiBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGRhdGEgYW5kIGlzIGRyYXduIG9uIGEgc3BlY2lmaWMgY29vcmRpbmF0ZXMgc3lzdGVtLg0KDQpgZ2dwbG90MmAgVGVybWlub2xvZ3k6IA0KDQotICoqZ2dwbG90KiogLSB0aGUgbWFpbiBmdW5jdGlvbiB3aGVyZSB5b3Ugc3BlY2lmeSB0aGUgZGF0YXNldCBhbmQgdmFyaWFibGVzIHRvIHBsb3QgKHRoaXMgaXMgd2hlcmUgd2UgZGVmaW5lIHRoZSBgeGAgYW5kDQpgeWAgdmFyaWFibGUgbmFtZXMpDQotICoqZ2VvbXMqKiAtIGdlb21ldHJpYyBvYmplY3RzDQogICAgLSBlLmcuIGBnZW9tX3BvaW50KClgLCBgZ2VvbV9iYXIoKWAsIGBnZW9tX2xpbmUoKWAsIGBnZW9tX2hpc3RvZ3JhbSgpYA0KLSAqKmFlcyoqIC0gYWVzdGhldGljcw0KICAgIC0gc2hhcGUsIHRyYW5zcGFyZW5jeSwgY29sb3IsIGZpbGwsIGxpbmUgdHlwZXMNCi0gKipzY2FsZXMqKiAtIGRlZmluZSBob3cgeW91ciBkYXRhIHdpbGwgYmUgcGxvdHRlZA0KICAgIC0gY29udGludW91cywgZGlzY3JldGUsIGxvZywgZXRjDQoNClRoZSBmdW5jdGlvbiBgYWVzKClgIGlzIGFuIGFlc3RoZXRpYyBtYXBwaW5nIGZ1bmN0aW9uIGluc2lkZSB0aGUgYGdncGxvdCgpYCBvYmplY3QuIA0KV2UgdXNlIHRoaXMgZnVuY3Rpb24gdG8gc3BlY2lmeSBwbG90IGF0dHJpYnV0ZXMgKGUuZy4gYHhgIGFuZCBgeWAgdmFyaWFibGUgbmFtZXMpIHRoYXQgd2lsbCBub3QgY2hhbmdlIGFzIHdlIGFkZCBtb3JlIGxheWVycy4gIA0KDQpBbnl0aGluZyB0aGF0IGdvZXMgaW4gdGhlIGBnZ3Bsb3QoKWAgb2JqZWN0IGJlY29tZXMgYSBnbG9iYWwgc2V0dGluZy4gDQpGcm9tIHRoZXJlLCB3ZSB1c2UgdGhlIGBnZW9tYCBvYmplY3RzIHRvIGFkZCBtb3JlIGxheWVycyB0byB0aGUgYmFzZSBgZ2dwbG90KClgIG9iamVjdC4gDQpUaGVzZSB3aWxsIGRlZmluZSB3aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGlsbHVzdHJhdGluZyB1c2luZyB0aGUgZGF0YS4NCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KU28gZm9yIHRoaXMgZmlyc3QgcGxvdCB3ZSBqdXN0IHdhbnQgdG8gcmVjcmVhdGUgdGhlIHBsb3QgaW4gdGhlIHJlcG9ydC4gV2Ugd2lsbCB1c2UgdGhlIGBtYWpvcl9ncm91cHNfbG9uZ2AgdGliYmxlIGFuZCB3ZSB3aWxsIGZpbHRlciBmb3Igb25seSB0aGUgYCJBbGwiYCB2YWx1ZXMgb2YgdGhlIGBHZW5kZXJgIGNvbHVtbi4NCldlIGFsc28gd2FudCB0byBleGNsdWRlIHRoZSBkYXRhIGZvciB0aGUgVW5pdGVkIHN0YXRlcy4gDQpXZSBjYW4gZXhjbHVkZSB0aGUgZGF0YSBmcm9tIHRoZSBVUyB1c2luZyB0aGUgbm90IGVxdWFscyBgIT1gIG9wZXJhdG9yIGxpa2Ugc286DQoNCmBgYHtyfQ0KbWFqb3JfZ3JvdXBzX2xvbmcgJT4lDQogICBmaWx0ZXIoR2VuZGVyID09ICJBbGwiLA0KICAgICAgICAgIEdyb3VwICE9ICJVbml0ZWQgU3RhdGVzIikNCmBgYA0KDQpUaGUgZGF0YSBmcm9tIHRoZSB0YWJsZXMgb25seSBpbmNsdWRlZCB1cCB0byAyMDE3LCBidXQgdGhpcyB3aWxsIHN0aWxsIGFsbG93IHVzIHRvIGNyZWF0ZSBhIHNpbWlsYXIgcGxvdC4NCg0KTm93IHdlIGhhdmUgdGhlIGRhdGEgcmVhZHkgdG8gbWFrZSB0aGUgcGxvdC4uLiBidXQgaG93IGRvIHdlIGFjdHVhbGx5IHN0YXJ0IG1ha2luZyB0aGUgcGxvdD8NCg0KRmlyc3QsIHdlIHN0YXJ0IHdpdGggdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gb2YgdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlLg0KDQpUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIHRoYXQgdGhlIGFlc3RoZXRpY3MgYGFlcygpYCBiZSBzcGVjaWZpZWQuIFRoaXMgaW52b2x2ZXMgY2hvb3Npbmcgd2hhdCB2YXJpYWJsZSB3aWxsIGJlIHBsb3R0ZWQgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHkgYXhpcy4gSXQgYWxzbyBpbnZvbHZlcyBjaG9vc2luZyB2YXJpYWJsZXMgdG8gY29sb3Igb3IgZ3JvdXAgb3VyIHBsb3QgYnkuIA0KDQpJbiBvdXIgY2FzZSwgd2Ugd2FudCB0byBwbG90IHRoZSBwZXJjZW50IG9mIGRpc2Nvbm5lY3Rpb24gb24gdGhlIHktYXhpcyAodGh1cyB0aGUgYFBlcmNlbnRgIHZhcmlhYmxlIG9mIHRoZSBgbWFqb3JfZ3JvdXBzX2xvbmdgIGRhdGEpIGFuZCB0aGUgYFllYXJgIG9uIHRoZSB4LWF4aXMuIFdlIHdhbnQgdG8gc2VwYXJhdGUgZWFjaCByYWNpYWwgb3IgZXRobmljIGdyb3VwIHRvIGhhdmUgdGhlaXIgb3duIHBvaW50cy9saW5lcy4gVGh1cyB3ZSB3aWxsIHVzZSB0aGUgYGNvbG9yYCBhcmd1bWVudCBmb3IgdGhpcyB2YXJpYWJsZSwgYXMgd2UgaW50ZW5kIHRvIGNvbG9yIG91ciBwbG90IHNvbWV0aGluZyBvdGhlciB0aGFuIGJsYWNrLg0KDQpJZiB3ZSBydW4gdGhlIGZvbGxvd2luZyBjb2RlLCB3ZSBnZXQgYW4gZW1wdHkgcGxvdC4NCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCm1ham9yX2dyb3Vwc19sb25nICU+JQ0KICAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwNCiAgICAgICAgICBHcm91cCAhPSAiVW5pdGVkIFN0YXRlcyIpICU+JQ0KZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IFBlcmNlbnQsIGNvbG9yID0gUmFjZV9FdGhuaWNpdHkpKQ0KYGBgDQoNCg0KVGhlIG5leHQgdGhpbmcgd2UgbmVlZCB0byBkbyBpcyBhZGQgYGdncGxvdDJgIGxheWVycyB1c2luZyB0aGUgYCtgIG9wZXJhdG9yIHRvIHNwZWNpZnkgaG93IHdlIHdhbnQgdGhlIGRhdGEgdG8gYmUgZGlzcGxheWVkIG9uIG91ciBwbG90Lg0KDQpXZSB3b3VsZCBsaWtlIGJvdGggcG9pbnRzIGFuZCBsaW5lcy4gV2Ugd2lsbCB1c2UgdGhlIGBnZW9tX3BvaW50KClgIGFuZCBgZ2VvbV9saW5lKClgIGZ1bmN0aW9ucyBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2UgdG8gZG8gdGhpcy4gVGhlIGxheWVyIHdlIGFkZCBmaXJzdCB3aWxsIGJlIHBsb3R0ZWQgZmlyc3QgYW5kIHNvIG9uIHdpdGggdGhlIG5leHQgbGF5ZXJzLiBXZSB3aWxsIGFsc28gc3BlY2lmeSB0aGUgc2l6ZSBvZiB0aGVzZSBlbGVtZW50cyB1c2luZyB0aGUgYHNpemVgIGFyZ3VtZW50Lg0KDQpgYGB7cn0NCm1ham9yX2dyb3Vwc19sb25nICU+JQ0KICBmaWx0ZXIoR2VuZGVyID09ICJBbGwiLCBHcm91cCAhPSAiVW5pdGVkIFN0YXRlcyIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gUGVyY2VudCwgY29sb3IgPSBSYWNlX0V0aG5pY2l0eSkpICsNCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDAuNSkgKw0KICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMpIA0KYGBgDQoNCk9LLCBub3QgYmFkLCBidXQgd2UgaGF2ZSBxdWl0ZSBhIGJpdCB0byB3b3JrIG9uIHRvIG1ha2UgdGhlIHBsb3Qgc3R5bGUgbWF0Y2guDQoNCkZpcnN0LCB3ZSB3aWxsIHVwZGF0ZSB0aGUgeC1heGlzIGFuZCB5LWF4aXMsIHRvIGxvb2sgbW9yZSBzaW1pbGFyIHRvIHRoZSBwbG90IGZyb20gdGhlIHJlcG9ydC4gV2UgY2FuIHNwZWNpZnkgdGhlIGxvY2F0aW9uIG9mIHRoZSB0aWNrIG1hcmtzIHVzaW5nIGBicmVha3NgIGFyZ3VtZW50IG9mIHRoZSBgc2NhbGVfeF9jb250aW51b3VzKClgIGFuZCBgc2NhbGVfeV9jb250aW51b3VzKClgIGZ1bmN0aW9ucyAoYWxzbyBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2UpLiBUaGVzZSBmdW5jdGlvbnMgYWxzbyBhbGxvdyBmb3Igc3BlY2lmaWNhdGlvbiBvZiB0aGUgcmFuZ2Ugb3IgbGltaXRzIG9mIHRoZSBheGlzIHVzaW5nIHRoZSBgbGltaXRzYCBhcmd1bWVudC4gV2UgY2FuIHVzZSB0aGUgYmFzZSBgc2VxKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIHNlcXVlbmNlIG9mIG51bWJlcnMgZm9yIGVhY2ggdGljayBtYXJrLg0KV2Ugd2lsbCBtYWtlIHRoZSB4LWF4aXMgdXBwZXIgbGltaXQgYSBiaXQgbGFyZ2VyIHRvIGFsbG93IGZvciB0aGUgaW1hZ2Ugb2YgdGhlIGZpZ3VyZSBhbmQgdGhlIGxhYmVsIGZvciBlYWNoIGdyb3VwLg0KDQpOZXh0LCB3ZSB3aWxsIGNoYW5nZSB0aGUgY29sb3IgdG8gbWF0Y2ggdGhlIG9uZSB0aGF0IHdlIGlkZW50aWZpZWQsIGJ5IHVzaW5nIHRoZSBgc2NhbGVfY29sb3JfbWFudWFsKClgIGZ1bmN0aW9uIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4gVGhpcyByZXF1aXJlcyBjb2xvciB2YWx1ZXMgZm9yIGVhY2ggZ3JvdXAuIEluIG91ciBjYXNlLCB3ZSBoYXZlIDUgZ3JvdXBzLCBzbyB3ZSByZXBlYXQgdGhpcyB2YWx1ZSA1IHRpbWVzIHVzaW5nIHRoZSBiYXNlIGByZXAoKWAgZnVuY3Rpb24uDQoNCldlIHdpbGwgYWxzbyBjaGFuZ2UgdGhlIG92ZXJhbGwgbG9vayBvZiB0aGUgcGxvdCB1c2luZyB0aGUgYHRoZW1lX2NsYXNzaWMoKWAgZnVuY3Rpb24uIFNlZSBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dndGhlbWUuaHRtbCkgZm9yIGEgbGlzdCBvZiBvcHRpb25zLg0KDQpgYGB7cn0NCiB5b3V0aF9wbG90IDwtIA0KICBtYWpvcl9ncm91cHNfbG9uZyAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgR3JvdXAgIT0gIlVuaXRlZCBTdGF0ZXMiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IFBlcmNlbnQsIGNvbG9yID0gUmFjZV9FdGhuaWNpdHkpKSArDQogICAgZ2VvbV9saW5lKCBzaXplID0gMC41KSArDQogICAgZ2VvbV9wb2ludCggc2l6ZSA9IDMpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDIwMDgsMjAxOCwgYnk9MSksDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMjAwOCwyMDIwKSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoNSwzMCwgYnkgPTUpLA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDUsMzApKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocmVwKCAiIzAwODM5MyIsIDUpKSkgKyANCiAgICB0aGVtZV9jbGFzc2ljKCkNCg0KeW91dGhfcGxvdA0KYGBgDQoNCk5vdyBsZXQncyBhZGQgc29tZSBsYWJlbHMuIFdlIGNhbiBhZGQgYSB0aXRsZSB1c2luZyB0aGUgYHRpdGxlYCBhcmd1bWVudCBhbmQgYSB5LWF4aXMgdGl0bGUgdXNpbmcgdGhlIGB5YCBhcmd1bWVudCBvZiB0aGUgYGxhYnMoKWAgZnVuY3Rpb24uDQpgYGB7cn0NCnlvdXRoX3Bsb3QgPC0gDQogIHlvdXRoX3Bsb3QgKyANCiAgbGFicyh0aXRsZSA9ICJZT1VUSCBESVNDT05ORUNUSU9OIEJZIFJBQ0UgQU5EIEVUSE5JQ0lUWSwgMjAwOCAtIDIwMTciLA0KICAgICAgICAgICB5ID0gIllPVVRIIERJU0NPTk5FQ1RJT04gKCUpIikgKw0KICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMwMDgzOTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQ0KDQp5b3V0aF9wbG90IDwtIA0KICBkaXJlY3RsYWJlbHM6OmRpcmVjdC5sYWJlbCh5b3V0aF9wbG90LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdChkbC50cmFucyh4ID0geCsxLjIsIHkgPSB5ICsgMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsYXN0LnBvaW50cyIpKQ0KeW91dGhfcGxvdA0KYGBgDQoNCkdldHRpbmcgY2xvc2UhDQoNCk5vdyB3ZSBuZWVkIHRvIGFkZCBmaWd1cmluZSBpY29ucyB0byB0aGUgcGxvdC4gIA0KDQpUbyBhZGQgdGhlIGZpZ3VyaW5lcyB3ZSBjYW4gdXNlIGljb25zIGZyb20gZnJlZSByZXNvdXJjZXMgbGlrZSBbZm9udCBhd2Vzb21lXShodHRwczovL2ZvbnRhd2Vzb21lLmNvbS8pLiBTZWUgW2hlcmVdKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvQ2F0ZWdvcnk6Rm9udF9Bd2Vzb21lXzRfaWNvbnMpIGZvciBhIGxpbmsgdG8gYHN2Z2Agb3B0aW9ucy4gT3Igd2UgY291bGQgYWxzbyB0YWtlIGEgc2NyZWVuc2hvdCBvZiBqdXN0IHRoZSBmaWd1cmluZSBmcm9tIHRoZSBvcmlnaW5hbCBwbG90LiBIb3dldmVyLCBpdCBpcyB1c2VmdWwgdG8ga25vdyBob3cgdG8gY3JlYXRlIHNpbWlsYXIgaWNvbnMgaWYgd2UgZGlkbid0IGhhdmUgdGhlIG9yaWdpbmFsIHBsb3QgdG8gd29yayB3aXRoIHNvIHdlIHdpbGwgc3RhcnQgd2l0aCBhbiBleGFtcGxlIG9mIHVzaW5nIGEgZm9udCBhd2Vzb21lIGljb24uIA0KDQoNCiMjIyMjIFVzaW5nIGZvbnQgYXdlc29tZToNCkZpcnN0IHdlIG5lZWQgdG8gaW1wb3J0IHRoZSBpbWFnZSBmcm9tIGZvbnQgYXdlc29tZToNCg0KYGBge3IsIG91dC53aWR0aD0iMTAlIn0NCg0KZmFfZmlndXJpbmUgPC0gaW1hZ2VfcmVhZCgiaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy83LzdjL1VzZXJfZm9udF9hd2Vzb21lLnN2ZyIpDQoNCmZhX2ZpZ3VyaW5lDQpgYGANCg0KV2Ugd291bGQgbGlrZSB0byBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBpY29uIHRvIG1hdGNoIHRoYXQgb2Ygb3VyIHBsb3QuIFdlIGNhbiBkbyBzbyB1c2luZyB0aGUgYGltYWdlX2ZpbGwoKWAgZnVuY3Rpb24gb2YgdGhlIGBtYWdpY2tgIHBhY2thZ2UuIFdlIG5lZWQgdG8gbGlzdCB0aGUgbmFtZSBvZiB0aGUgaW1hZ2Ugb2JqZWN0IHRvIGNoYW5nZSwgdGhlbiB3ZSBuZWVkIHRvIGxpc3QgdGhlIGNvbG9yIHdlIHdhbnQgdG8gdXNlLiBGaW5hbGx5IHdlIG5lZWQgdG8gbGlzdCB3aGF0IHBvaW50cyB3ZSB3YW50IHRvIGNvbG9yLiBXZSBkb24ndCB3YW50IHRvIGNvbG9yIHRoZSBzcGFjZSBhcm91bmQgdGhlIGJsYWNrIHNoYXBlcywganVzdCB0aGUgYmxhY2sgc2hhcGVzIHRoZW1zZWx2ZXMuIEl0IG1heSB0YWtlIHNvbWUgdHJpYWwgYW5kIGVycm9yIHRvIGZpZ3VyZSBvdXQgd2hlcmUgdGhlc2UgcGl4ZWxzIGFyZS4NCg0KQWNjb3JkaW5nIHRvIHRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgYG1hZ2lja2AgcGFja2FnZToNCg0KPiBUaGUgYGltYWdlX2ZpbGwoKWAgZnVuY3Rpb24gcGVyZm9ybXMgZmxvb2QtZmlsbCBieSBwYWludGluZyBzdGFydGluZyBwb2ludCBhbmQgYWxsIG5laWdoYm9yaW5nIHBpeGVscyBvZiBhcHByb3hpbWF0ZWx5IHRoZSBzYW1lIGNvbG9yLg0KDQo+IHBvaW50ID0gYSBnZW9tZXRyeV9wb2ludCBzdHJpbmcgaW5kaWNhdGluZyB0aGUgc3RhcnRpbmcgcG9pbnQgb2YgdGhlIGZsb29kLWZpbGwgDQoNCkZpcnN0IHdlIHdpbGwgY29sb3IgaW4gdGhlIGNpcmNsZSBhdCB0aGUgdG9wIG9mIHRoZSBpY29uLiBJZiB3ZSB1c2UgYHBvaW50ID0gKzYwMCs2MDBgIHRoaXMgaXMgc3RhcnRpbmcgYXQgdGhlIHBpeGVsIGF0IHg6NjAwLCB5OjYwMC4gV2UgY2FuIHNlZSBmcm9tIHRoZSBvdXRwdXQgYWJvdXQgdGhlIGBmYV9maWd1cmluZWAgdGhhdCB0aGUgaW1hZ2UgaXMgMTc5MiBieSAxNzkyIHBpeGVscyBpbiBzaXplLiBUaHVzLCBpZiB3ZSBjaG9vc2UgYSBwaXhlbCB0aGF0IGlzIHdpdGhpbiB0aGUgYm9yZGVycyBvZiB0aGUgc2hhcGUgd2Ugd2FudCB0byBjaGFuZ2UgdGhlbiB3ZSBjYW4gY2hhbmdlIHRoYXQgc2hhcGUuIFdlIGNhbiB1c2UgdGhlIHNpemUgb2YgdGhlIGltYWdlIHRvIGZpZ3VyZSBvdXQgd2hlcmUgdGhlIHBpeGVsIG1pZ2h0IGJlLiBTaW5jZSB0aGUgaW1hZ2UgaXMgMTc5MiBieSAxNzkyIHBpeGVscywgODAwIGlzIGFib3V0IGhhbGYgd2F5IHRocm91Z2ggdGhlIGltYWdlIGltYWdlIGZyb20gdGhlIGxlZnQgc2lkZSAoeDowKSBhbmQgNjAwIGlzIGFib3V0IGEgdGhpcmQgb2YgdGhlIHdheSB0aHJvdWdoIHRoZSBhbmQgdGhlIHRvcCAoeTowKS4NCg0KDQoNCmBgYHtyLCBvdXQud2lkdGg9IjEwJSJ9DQpmYV9maWd1cmluZSA8LSBpbWFnZV9maWxsKGZhX2ZpZ3VyaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMDA4MzkzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50ID0gIis4MDArNjAwIikNCg0KZmFfZmlndXJpbmUNCg0KYGBgDQoNClNlY29uZCB3ZSB3aWxsIGNvbG9yIGluIHRoZSBsb3dlciBzaGFwZSBvZiB0aGUgaWNvbi4gVGhpcyBzaGFwZSBpcyBpbiB0aGUgbG93ZXIgaGFsZiBvZiB0aGUgaW1hZ2UgYW5kIGluIHRoZSBtaWRkbGUgb2YgdGhlIGltYWdlIGFjY29yZGluZyB0byB0aGUgeCBheGlzLCB0aHVzIHdlIHdpbGwgc3RhcnQgYXQgdGhlIHBpeGVsIHRoYXQgaXMgYXQgYWJvdXQgaGFsZiB3YXkgZm9yIHRoZSB3aWR0aCBhdCB4OjgwMCBhbmQgYSBiaXQgbW9yZSB0aGFuIGhhbGYgd2F5IGRvd24gZnJvbSB0aGUgdG9wICh5OjApIGZvciB0aGUgaGVpZ2h0IHk6MTAwMC4gDQoNCmBgYHtyLCBvdXQud2lkdGg9IjEwJSJ9DQpmYV9maWd1cmluZSA8LSBpbWFnZV9maWxsKGZhX2ZpZ3VyaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMDA4MzkzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50ID0gIis4MDArMTAwMCIpDQoNCmZhX2ZpZ3VyaW5lDQpgYGANCg0KTmljZSEgV2UgY2hhbmdlZCB0aGUgY29sb3Igb2YgdGhlIGltYWdlLg0KDQpOb3cgbGV0J3MgdHJ5IHVzaW5nIGEgc2NyZWVuc2hvdCBvZiB0aGUgb3JpZ2luYWwgcGxvdC4gWW91IGNhbiBmaW5kIG91ciBzY3JlZW5zaG90IGluIG91ciBgaW1nYCBkaXJlY3Rvcnkgb24gb3VyIEdpdEh1YiByZXBvc2l0b3J5Lg0KDQojIyMjIFVzaW5nIGEgc2NyZWVuc2hvdDoNCmBgYHtyLCBvdXQud2lkdGg9ICIxMCUifQ0KDQpmaWd1cmluZSA8LWltYWdlX3JlYWQoaGVyZTo6aGVyZSgiaW1nIiwgImZpZ3VyaW5lLnBuZyIpKQ0KZmlndXJpbmUNCmBgYA0KDQpOb3cgdG8gYWRkIHRoZSBpbWFnZSBvZiB0aGUgZmlndXJpbmUgdG8gdGhlIHBsb3Qgd2UgY2FuIHVzZSB0aGUgYGRyYXdfaW1hZ2UoKWAgZnVuY3Rpb24gb2YgdGhlIGBjb3dwbG90YCBwYWNrYWdlLiBXZSBzaW1wbHkgbmVlZCB0byBzcGVjaWZ5IHRoZSBuYW1lIG9mIHRoZSBpbWFnZSwgYW5kIHRoZW4gd2hlcmUgd2Ugd291bGQgbGlrZSBpdCB0byBnbyBhY2NvcmRpbmcgdG8gdGhlIHgtIGFuZCB5LWF4aXMgYW5kIGZpbmFsbHkgdGhlIHNjYWxlZCBzaXplIG9mIHRoZSBpbWFnZS4gVGhpcyBgc2NhbGVgIGFyZ3VtZW50IGFsc28gdGFrZXMgYSBiaXQgb2YgdHJpYWwgYW5kIGVycm9yLiANCg0KSGVyZSB3ZSB3aWxsIGFkZCBib3RoIGltYWdlcyBvZiB0aGUgZmlndXJpbmVzIHRvIGEgY291cGxlIG9mIGdyb3VwczoNCg0KYGBge3J9DQp5b3V0aF9wbG90ICsNCiAgY293cGxvdDo6ZHJhd19pbWFnZShmaWd1cmluZSwgeCA9IDIwMTcsIHkgPSAyMy41LCBzY2FsZSA9IDQpICsNCiAgY293cGxvdDo6ZHJhd19pbWFnZShmYV9maWd1cmluZSwgeCA9IDIwMTcsIHkgPSAxNy41LCBzY2FsZSA9IDIpDQpgYGANCg0KTmljZSENCg0KTm93IHdlIGp1c3QgbmVlZCB0byBhZGQgdGhpcyBmb3IgYWxsIG9mIHRoZSBncm91cHMgYW5kIHdlIGFsc28gd2lsbCB3YW50IHRvIHJlcGxhY2Ugb3VyIGBOYXRpdmUgQW1lcmljYW5gIGxhYmVsIHdpdGggYSB0d28gbGluZSB2ZXJzaW9uIHNvIHRoYXQgd2UgbG9vayBhcyBzaW1pbGFyIHRvIHRoZSByZXBvcnQgcGxvdCBhcyBwb3NzaWJsZS4NCg0KIyMjIyB7LnRoaW5rX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkhvdyBtaWdodCB3ZSBjaGFuZ2UgdGhlIE5hdGl2ZSBBbWVyaWNhbiBsYWJlbCB0byBoYXZlIHR3byBsaW5lcz8gKGhpbnQ6IHRoaW5rIGFib3V0IGhvdyB3ZSBzZXBhcmF0ZWQgdGhlIHJvd3Mgb2Ygb3VyIHRhYmxlcykNCg0KIyMjIw0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PkNsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLjwvc3VtbWFyeT4NCg0KYGBge3J9DQp5b3V0aF9wbG90IDwtIG1ham9yX2dyb3Vwc19sb25nICU+JQ0KICAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwNCiAgICAgICAgICBHcm91cCAhPSAiVW5pdGVkIFN0YXRlcyIpICU+JQ0KICAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gUmFjZV9FdGhuaWNpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIk5hdGl2ZSBBbWVyaWNhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiTmF0aXZlXG5BbWVyaWNhbiIpKSAgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gUGVyY2VudCwgY29sb3IgPSBSYWNlX0V0aG5pY2l0eSkpICsNCiAgICBnZW9tX2xpbmUoIHNpemUgPSAwLjUpICsNCiAgICBnZW9tX3BvaW50KCBzaXplID0gMykgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwOCwyMDE4LCBieT0xKSwNCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDIwMDgsMjAxOSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDUsMzAsIGJ5ID01KSwNCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDUsMzApKSArDQogICAgdGhlbWVfY2xhc3NpYygpICsNCiAgICBsYWJzKHRpdGxlID0gIllPVVRIIERJU0NPTk5FQ1RJT04gQlkgUkFDRSBBTkQgRVRITklDSVRZLCAyMDA4IC0gMjAxNyIsDQogICAgICAgICB5ID0gIllPVVRIIERJU0NPTk5FQ1RJT04gKCUpIikgKw0KICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMwMDgzOTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhyZXAoICIjMDA4MzkzIiwgNSkpKSsNCiAgICBkcmF3X2ltYWdlKGZpZ3VyaW5lLCB4ID0gMjAxNy4yNSwgeSA9IDIzLjUsIHNjYWxlID0gNCkgKw0KICAgIGRyYXdfaW1hZ2UoZmlndXJpbmUsIHggPSAyMDE3LjI1LCB5ID0gMTcuNSwgc2NhbGUgPSA0KSArDQogICAgZHJhd19pbWFnZShmaWd1cmluZSwgeCA9IDIwMTcuMjUsIHkgPSAxMy41LCBzY2FsZSA9IDQpICsNCiAgICBkcmF3X2ltYWdlKGZpZ3VyaW5lLCB4ID0gMjAxNy4yNSwgeSA9IDksIHNjYWxlID0gNCkgKw0KICAgIGRyYXdfaW1hZ2UoZmlndXJpbmUsIHggPSAyMDE3LjI1LCB5ID0gNS41LCBzY2FsZSA9IDQpDQoNCnlvdXRoX3Bsb3QgPC0gZGlyZWN0bGFiZWxzOjpkaXJlY3QubGFiZWwoeW91dGhfcGxvdCwgbGlzdChkbC50cmFucyh4ID0geCArIDEuNCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHkgKyAwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhc3QucG9pbnRzIikpDQpgYGANCg0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3J9DQp5b3V0aF9wbG90DQpgYGANCk5pY2UhIE91ciBwbG90IGxvb2tzIHZlcnkgc2ltaWxhciBzdHlsaXN0aWNhbGx5IHRvIHRoZSBwbG90IGluIHRoZSByZXBvcnQuIA0KDQpXZSBjYW4gc2VlIGZyb20gdGhpcyBwbG90IHRoYXQgTmF0aXZlIEFtZXJpY2FucyBoYXZlIGEgdmVyeSBoaWdoIHJhdGUgb2YgeW91dGggZGlzY29ubmVjdGlvbiwgd2l0aCByb3VnaGx5IHN0aWxsIGEgcXVhcnRlciBvZiB0aGUgcG9wdWxhdGlvbiBleHBlcmllbmNpbmcgeW91dGggZGlzY29ubmVjdGlvbiBpbiB0aGlzIHN1cnZleS4gQWx0aG91Z2ggc29tZSBncm91cHMgc2hvdyBhIGRvd253YXJkIHRyZW5kLCBsaWtlIHRoZSBMYXRpbnggZ3JvdXAsIHRoZSBsZXZlbCBoYXMgYmVlbiBmYWlybHkgc3RhYmxlIHNpbmNlIDIwMDgsIGFzIHdlIHNlZSB0aGF0IHRoZSBzbG9wZSBvZiBlYWNoIGxpbmUgaXMgZmFpcmx5IGZsYXQuIFdlIHdpbGwgaW52ZXN0aWdhdGUgdGhpcyBmdXJ0aGVyIGluIG91ciBhbmFseXNpcy4gDQoNCiMjIyAqKk1ham9yIFJhY2UgYW5kIEV0aG5pYyBHcm91cHMgYW5kIEdlbmRlciBQbG90KioNCioqKg0KTm93IGxldCdzIGNyZWF0ZSBhIHNpbWlsYXIgcGxvdCBmb3IgZmVtYWxlcyBhbmQgbWFsZXMuIFRvIGRvIHRoaXMgd2UgY2FuIHNpbXBseSB1c2UgdGhlIGBmYWNldF93cmFwKClgIGZ1bmN0aW9uLiBXZSBuZWVkIHRvIHNwZWNpZnkgd2hhdCB2YXJpYWJsZSB3ZSB3YW50IHRvIGZhY2V0IGJ5LCBpbiB0aGlzIGNhc2UgdGhlIGBHZW5kZXJgIHZhcmlhYmxlLCBieSB1c2luZyB0aGUgYH5gIG9wZXJhdG9yLiBXZSBhbHNvIHdhbnQgdG8gc2V0IHRoZSBgc2NhbGVzYCBhcmd1bWVudCB0byBgImZyZWUiYCwgc28gdGhhdCB3ZSBoYXZlIGEgeS1heGlzIGZvciBib3RoIHRoZSBmZW1hbGUgYW5kIG1hbGUgcGxvdHMuIFNlZSB0aGlzIFtjYXNlIHN0dWR5XShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vb2NzLWJwLWNvMi1lbWlzc2lvbnMvI0ZhY2V0ZWRfcGxvdHMpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGZhY2V0aW5nIHBsb3RzLg0KDQpXZSBkbyBub3Qgd2FudCB0byBwbG90IHRoZSBgIkFsbCJgIHZhbHVlcyBmb3IgdGhlIGBHZW5kZXJgIHZhcmlhYmxlIGFzIHdlIGhhdmUgYWxyZWFkeSBzaG93biB0aGF0IGluIHRoZSBwcmV2aW91cyBwbG90LCBzbyB3ZSBhZ2FpbiBleGNsdWRlIGl0LiANCg0KIyMjIyB7LnJlY2FsbF9jb2RlX3F1ZXN0aW9uX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkhvdyBkbyB3ZSBtb2RpZnkgdGhlIHBsb3QgYWJvdmUgdG8gbm90IGluY2x1ZGUgdGhlIGAiQWxsImAgdmFsdWVzIGZvciB0aGUgYEdlbmRlcmAgdmFyaWFibGUgYW5kIHN0cmF0aWZ5IGJ5IGBHZW5kZXJgPw0KDQojIyMjDQoNCioqKg0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+Q2xpY2sgaGVyZSB0byByZXZlYWwgdGhlIGNvZGUuPC9zdW1tYXJ5Pg0KDQpgYGB7cn0NCnBsb3RfYnlfZ2VuZGVyIDwtIG1ham9yX2dyb3Vwc19sb25nICU+JQ0KICAgZmlsdGVyKFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiLCBHZW5kZXIgIT0gIkFsbCIpICU+JQ0KICAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gUmFjZV9FdGhuaWNpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIk5hdGl2ZSBBbWVyaWNhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiTmF0aXZlXG5BbWVyaWNhbiIpLA0KICAgICAgICAgICAgICAgICAgR2VuZGVyID0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gR2VuZGVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJGZW1hbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIkZFTUFMRSIpLA0KICAgICAgICAgICAgICAgICAgR2VuZGVyID0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gR2VuZGVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJNYWxlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJNQUxFIikpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gUGVyY2VudCwgY29sb3IgPSBSYWNlX0V0aG5pY2l0eSkpICsNCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDAuNSkgKw0KICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgICBmYWNldF93cmFwKH5HZW5kZXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwOCwyMDE4LCBieT0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygyMDA4LDIwMjApKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDMwLCBieSA9NSksDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoNSwzMSkpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIGxhYnModGl0bGUgPSAiWU9VVEggRElTQ09OTkVDVElPTiBCWSBHRU5ERVIgQU5EIFJBQ0lBTC9FVEhOSUMgR1JPVVAiLA0KICAgICAgICAgeSA9ICJZT1VUSCBESVNDT05ORUNUSU9OICglKSIpICsNCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzAwODM5MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPWVsZW1lbnRfcmVjdChmaWxsPSIjMDAzNjYxIiksDQogICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAnd2hpdGUnLCBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMikpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocmVwKCAiIzAwODM5MyIsIDYpKSkNCiANCnBsb3RfYnlfZ2VuZGVyIDwtIGRpcmVjdC5sYWJlbChwbG90X2J5X2dlbmRlciwgbGlzdChkbC50cmFucyh4ID0geCswLjMsIHkgPSB5ICswKSwgImxhc3QucG9pbnRzIikpDQoNCmBgYA0KPC9kZXRhaWxzPg0KDQoqKioNCg0KYGBge3J9DQpwbG90X2J5X2dlbmRlcg0KYGBgDQoNCg0KSXQncyBhIGJpdCBkaWZmaWN1bHQgdG8gdGVsbCB0aGUgQmxhY2sgZmVtYWxlIGFuZCBMYXRpbnggZmVtYWxlIGxpbmVzIGFwYXJ0LiANCg0KTGV0J3MgdHJ5IGFkZGluZyBhbm90aGVyIGNvbG9yIGZvciBldmVyeSBvdGhlciBsaW5lLiBBZ2FpbiB3ZSB3aWxsIHVzZSB0aGUgYHNjYWxlX2NvbG9yX21hbnVhbCgpYCBmdW5jdGlvbiB0byBzcGVjaWZ5IHRoZSBjb2xvcnMgb2YgdGhlIGxpbmVzIGFuZCBwb2ludHMgb24gdGhlIHBsb3QuICANCg0KV2Ugd2lsbCB1c2UgYmxhY2sgYXMgdGhlIG90aGVyIGNvbG9yLiBXZSBjYW4gdXNlIHRoZSBbaGV4IHRyaXBsZXRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9jb2xvcnM/b2xkZm9ybWF0PXRydWUpIGNvZGUgb3Igd2UgY2FuIHdyaXRlIG91dCB0aGUgbmFtZSBvZiB0aGUgY29sb3IsIGluIHRoaXMgY2FzZSB3ZSB3aWxsIHdyaXRlIGAiYmxhY2siYC4gDQpUbyBzcGVjaWZpY2FsbHkgY29sb3IgZWFjaCBncm91cCBkaWZmZXJlbnRseSwgd2UgY2FuIHdyaXRlIHRoZSBjb2xvcnMgaW4gdGhlIGFscGhhYmV0aWNhbGx5IG9yZGVyIG9mIHRoZSBkaXN0aW5jdCB2YWx1ZXMgb2YgYFJhY2VfRXRobmljaXR5YC4gVGh1cyB0aGUgZmlyc3QgY29sb3IgbGlzdGVkIHdpbGwgYmUgdGhlIGNvbG9yIGZvciB0aGUgYEFzaWFuYCB2YWx1ZXMsIHdoaWxlIHRoZSBsYXN0IHdpbGwgYmUgdGhlIGNvbG9yIGZvciB0aGUgYFdoaXRlYCB2YWx1ZXMuDQoNCg0KYGBge3J9DQpwbG90X2J5X2dlbmRlciA8LSANCiAgcGxvdF9ieV9nZW5kZXIgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwODM5MyIsImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMDA4MzkzIiwgIiMwMDgzOTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmxhY2siKSkNCmBgYA0KDQoNCiMjIyMgey5leHBsYWluX2Jsb2NrfQ0KPGI+PHU+IFF1ZXN0aW9uIE9wcG9ydHVuaXR5IDwvdT48L2I+DQoNCkNhbiB5b3UgZXhwbGFpbiB3aHkgd2UgZGlkbid0IHVzZSBhIHBpcGUgaW4gdGhlIHByZXZpb3VzIGNvZGUgY2h1bms/DQoNCiMjIyMNCg0KYGBge3J9DQpwbG90X2J5X2dlbmRlcg0KYGBgDQoNClZlcnkgaW50ZXJlc3RpbmchIEJ5IHBhcnNpbmcgdGhlIGdyb3VwcyBmdXJ0aGVyIGludG8gaW50ZXJzZWN0aW9ucyBvZiByYWNpYWwgYW5kIGV0aG5pYyBncm91cHMgd2l0aCBnZW5kZXIsIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBzb21lIHZlcnkgc3RyaWtpbmcgZGlmZmVyZW5jZXMgZm9yIHRoZSBCbGFjayBhbmQgTGF0aW54IGdyb3VwcyB3aGVuIHN0cmF0aWZpZWQgYnkgYEdlbmRlcmAuIE9uZSBwb3NpdGl2ZSB0aGluZyB0aGF0IHdlIGNhbiBzZWUsIGlzIHRoYXQgdGhlcmUgaGFzIGJlZW4gYSBzdGVlcCBkZWNsaW5lIGluIHlvdXRoIGRpc2Nvbm5lY3Rpb24gZm9yIExhdGlueCBmZW1hbGVzLiBVbmZvcnR1bmF0ZWx5LCB0aGUgbGV2ZWwgb2YgZGlzY29ubmVjdGlvbiBmb3IgZmVtYWxlcyBpbiAyMDA4IHdhcyBxdWl0ZSBoaWdoLCBzbyBub3cgTGF0aW54IGZlbWFsZXMgaGF2ZSBzaW1pbGFyIHJhdGVzIHRvIExhdGlueCBtYWxlcy4gSW4gY29udHJhc3QsIHdlIHNlZSB0aGF0IEJsYWNrIG1hbGVzIGhhdmUgaGFkIGFuZCBjb250aW51ZSB0byBoYXZlIG11Y2ggaGlnaGVyIHJhdGVzIG9mIGRpc2Nvbm5lY3Rpb24gdGhhbiBCbGFjayBmZW1hbGVzLiBCb3RoIEJsYWNrIGZlbWFsZXMgYW5kIG1hbGVzIHNob3cgYW4gaW5jcmVhc2VkIHJhdGUgc2luY2UgMjAxNi4gTmF0aXZlIEFtZXJpY2FuIGZlbWFsZXMgYWxzbyBzaG93IGFuIGluY3JlYXNlZCByYXRlIHNpbmNlIDIwMTYuIA0KDQoNCklmIHlvdSBoYXZlIHRpbWUsIHRoZSBuZXh0IHNlY3Rpb24gd2lsbCBzaG93IGhvdyB0byBtYWtlIGJhciBwbG90cyB0byBsb29rIGZvciBkaWZmZXJlbmNlcyBpbiBzdWJncm91cHMgZm9yIHRoZSBBc2lhbiBhbmQgTGF0aW54IGRhdGEuIE90aGVyd2lzZSwgeW91IGNhbiBza2lwIGFoZWFkIHRvIHRoZSBbKipEYXRhIEFuYWx5c2lzKipdIHNlY3Rpb24uDQoNCiMjIyAqKlN1Ymdyb3VwIHBsb3RzKioNCioqKg0KDQpJdCBpcyBjbGVhciBmcm9tIHRoZSBwcmV2aW91cyBwbG90IHRoYXQgb2JzZXJ2aW5nIG1vcmUgc3BlY2lmaWMgc3ViZ3JvdXBzIGluIG91ciBkYXRhIGNhbiBiZSB2ZXJ5IGluZm9ybWF0aXZlIQ0KDQpUaHVzLCB3ZSBhbHNvIHdhbnQgdG8gbWFrZSBwbG90cyBvZiB0aGUgQXNpYW4gYW5kIExhdGlueCBzdWJncm91cHMsIHRvIHNlZSBpZiB0aGVyZSBhcmUgcGFydGljdWxhciBldGhuaWMgZ3JvdXBzIHRoYXQgaGF2ZSBoaWdoZXIgbGV2ZWxzIG9mIHlvdXRoIGRpc2Nvbm5lY3Rpb24uIElmIHNvLCB0aGVzZSBncm91cHMgbWF5IHBhcnRpY3VsYXJseSBiZW5lZml0IGZvciBwcmV2ZW50aW9uIGFuZCByZS1lbmdhZ2VtZW50IGVmZm9ydHMuDQoNCldlIHdpbGwgYWxzbyBhdHRlbXB0IHRvIGNvbnRpbnVlIHRvIHBsb3QgdGhlIGdlbmRlcnMgc2VwYXJhdGVseSwgYXMgd2UgaGF2ZSBsZWFybmVkIHRoYXQgdGhlcmUgbWF5IGJlIGltcG9ydGFudCBnZW5kZXIgZ3JvdXAgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIHJhY2lhbCBhbmQgZXRobmljIGdyb3Vwcy4gSG93ZXZlciwgdGhlIGRhdGEgaXMgaW5jb21wbGV0ZSBmb3Igc29tZSBvZiB0aGUgZXRobmljIGdyb3Vwcy4gUmVjYWxsIHRoYXQgd2UgYWxzbyBvbmx5IGhhdmUgdHdvIHllYXJzIG9mIGRhdGEgZm9yIGJvdGggb3VyIEFzaWFuIGFuZCBMYXRpbnggc3ViZ3JvdXAgZGF0YS4gRmlyc3Qgd2Ugd2lsbCBzdGFydCBieSBwbG90dGluZyBqdXN0IHRoZSBzdWJncm91cHMgb3ZlciB0aGUgdHdvIHllYXJzLg0KDQpXZSBjYW4gY29udGludWUgdG8gbWFrZSBvdXIgcGxvdHMgbG9vayBsaWtlIHRoZXkgbWF0Y2ggdGhlIHJlcG9ydCBieSB1c2luZyBhIGNvbG9yIHBhbGV0dGUgYmFzZWQgb2ZmIHRoZSBjb2xvciB1c2VkIGluIHRoZSByZXBvcnQuIFdlIGNhbiB1c2UgdGhlIGBjb2xvclJhbXBQYWxldHRlKClgIGZ1bmN0aW9uIG9mIHRoZSBgZ3JEZXZpY2VzYCBwYWNrYWdlLCB3aGljaCBpcyBsb2FkZWQgYXV0b21hdGljYWxseSBpbiBhbiBSU3R1ZGlvIHNlc3Npb24uIFdlIGNhbiBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0aGUgY29sb3JzIHRvIHJhbmdlIGZyb20gZ3JheSB0byB0aGUgY29sb3IgdGhhdCB3ZSBoYXZlIGJlZW4gdXNpbmcuDQoNClRoZW4gd2UganVzdCBuZWVkIHRvIHNwZWNpZnkgaG93IG1hbnkgY29sb3JzIHdlIHdhbnQgaW4gb3VyIGdyYWRpZW50IHdoZW4gd2UgY2hvb3NlIHRvIHVzZSB0aGUgY29sb3IgcGFsZXR0ZS4gU2F5IHdlIHdhbnRlZCA0IGNvbG9ycywgd2UganVzdCBuZWVkIHRvIHR5cGUgYGN1c3RvbV9wYWwoNClgIHRvIGdldCBhIGxpc3Qgb2YgNCBjb2xvcnMgd2l0aGluIHRoZSBwYWxldHRlLiBUaHVzIHdlIHdpbGwgaGF2ZSBncmF5LCB0aGUgdGVhbCBjb2xvciB3ZSBoYXZlIGJlZW4gdXNpbmcsIGFuZCB0d28gc2hhZGVzIGluIGJldHdlZW4uIEFmdGVyIGRvaW5nIHRoaXMgd2UgY2hvc2UgdG8gdXNlIG9uZSBvZiB0aGUgY29sb3JzIGluIGJldHdlZW4gYW5kIHRvIHVzZSBqdXN0IHR3byBjb2xvcnMgZm9yIG91ciBwYWxldHRlLg0KDQpgYGB7cn0NCmN1c3RvbV9wYWwgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJncmF5IiwgIiMwMDgzOTMiKSkNCmN1c3RvbV9wYWwgPC0gY3VzdG9tX3BhbCg0KQ0KY3VzdG9tX3BhbA0KY3VzdG9tX3BhbCA8LSBjKCIjMDA4MzkzIiwiI0JFQkVCRSIpDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoZSBbaGV4IHRyaXBsZXRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9jb2xvcnM/b2xkZm9ybWF0PXRydWUpIGNvZGVzIGZvciBncmF5IGFuZCB0aGUgc2hhZGUgaW4gYmV0d2Vlbi4NCg0KT0ssIG5vdyBsZXQncyBzdGFydCB3aXRoIHRoZSBBc2lhbiBzdWJncm91cHMuIFNpbmNlIHdlIG9ubHkgaGF2ZSB0d28gdGltZSBwb2ludHMgb2YgZGF0YSBmb3IgZWFjaCBncm91cCwgd2Ugd2lsbCBjcmVhdGUgYSBiYXIgZ3JhcGggdGhpcyB0aW1lIHdoZXJlIHRoZSB0d28geWVhcnMgYXJlIGRpc3BsYXllZCBuZXh0IHRvIGVhY2ggb3RoZXIgZm9yIGVhY2ggeWVhci4gVGh1cywgd2Ugd2FudCB0aGUgYFllYXJgIHZhcmlhYmxlIHRvIGJlIGEgZGlzY3JldGUgdmFyaWFibGUuIFRvIGVuc3VyZSB0aGF0IGl0IGlzIGludGVycHJldGVkIGFzIGEgZGlzY3JldGUgdmFyaWFibGUsIHdlIG5lZWQgdG8gY29udmVydCBpdCB0byBiZSBhIGNoYXJhY3RlciB0eXBlIHZhcmlhYmxlIHJhdGhlciB0aGFuIG51bWVyaWMgdXNpbmcgdGhlIGJhc2UgYGFzLmNoYXJhY3RlcigpYCBmdW5jdGlvbi4gDQoNCg0KRmlyc3QsIHdlIHdhbnQgdG8gZmlsdGVyIGZvciB0aGUgcm93cyB3aGVyZSB0aGUgYEdlbmRlcmAgdmFyaWFibGUgdmFsdWVzIGFyZSBgQWxsYCwgd2UgYWxzbyBkbyBub3Qgd2FudCB0byBpbmNsdWRlIHRoZSBkYXRhIGZvciB0aGUgYXZlcmFnZSBvZiBhbGwgcmFjZXMuDQoNCmBgYHtyfQ0KYXNpYW5fc3ViZ3JvdXBzICU+JQ0KICBmaWx0ZXIoR2VuZGVyID09ICJBbGwiKSAlPiUNCiAgZmlsdGVyKFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiKSAlPiUNCiAgbXV0YXRlKFllYXIgPSBhcy5jaGFyYWN0ZXIoWWVhcikpDQpgYGANCg0KTm93IHRoYXQgd2UgaGF2ZSB3cmFuZ2xlZCB0aGUgZGF0YSB3ZSBjYW4gbWFrZSBvdXIgcGxvdC4gVG8gbWFrZSBhIGJhciBwbG90IHRoZXJlIGFyZSB0d28gbWFpbiBgZ2dwbG90MmAgZnVuY3Rpb25zLCAgYGdlb21fY29sKClgIGFuZCBgZ2VvbV9iYXIoKWAuIFRoZSBgZ2VvbV9jb2woKWAgZnVuY3Rpb24gcGxvdHMgdGhlIGFjdHVhbCB2YWx1ZXMgb2YgdGhlIGRhdGEsIHdoaWxlIHRoZSBgZ2VvbV9iYXIoKWBmdW5jdGlvbiBwbG90cyBjb3VudHMgKGhvd2V2ZXIgeW91IGNhbiBvdmVycmlkZSB0aGlzIHdpdGggdGhlIGBzdGF0ID0gaWRlbnRpdHlgIGFyZ3VtZW50KS4gV2UgYXJlIGludGVyZXN0ZWQgaW4gcGxvdHRpbmcgdGhlIGFjdHVhbCB2YWx1ZXMsIHNvIHdlIHdpbGwgdXNlIHRoZSBgZ29lbV9jb2woKWAgZnVuY3Rpb24uDQoNClRoaXMgdGltZSB3ZSB3aWxsIHNwZWNpZnkgdGhhdCB0aGUgYFllYXJgIHZhcmlhYmxlIGJlIHVzZWQgdG8gc3BlY2lmeSB0aGUgZmlsbCBjb2xvciBvZiB0aGUgYmFycyBvZiB0aGUgYmFyIHBsb3QgYnkgdXNpbmcgYGFlcyhmaWxsID0gWWVhcilgLg0KDQpXZSBhbHNvIG5lZWQgdG8gaW5kaWNhdGUgaG93IHRoZSBiYXJzIHNob3VsZCBiZSBwbG90dGVkIGJhc2VkIG9uIHRoZSBgcG9zaXRpb25gIGFyZ3VtZW50Lg0KDQpUaGUgb3B0aW9ucyBmb3IgdGhlIGBwb3NpdGlvbmAgYXJndW1lbnQgYXJlOg0KDQotIGBzdGFja2AgLSB0aGUgeWVhcnMgd291bGQgYmUgZGlzcGxheWVkIG9uIHRvcCBvZiBvbmUgYW5vdGhlciAodGhpcyBpcyBkZWZhdWx0KSAgIA0KLSBgZG9kZ2VgIC0gdGhlIHllYXJzIHdvdWxkIGJlIGRpc3BsYXllZCBuZXh0IHRvIG9uZSBhbm90aGVyIHdpdGggbm8gc3BhY2UgaW4gYmV0d2VlbiAgIA0KLSBgZG9kZ2UyYCAtIHRoZSB5ZWFycyB3b3VsZCBiZSBkaXNwbGF5ZWQgbmV4dCB0byBvbmUgYW5vdGhlciB3aXRoIGEgc3BhY2UgaW4gYmV0d2VlbiAgIA0KLSBgZmlsbGAgLSB0aGUgeWVhcnMgd291bGQgYmUgZGlzcGxheWVkIG9uIHRvcCBvZiBvbmUgb3RoZXIsIHdoZXJlIHRoZSBoZWlnaHRzIG9mIGVhY2ggY29sb3Igd291bGQgc2hvdyB0aGUgcmVsYXRpdmUgcHJvcG9ydGlvbnMgZm9yIGVhY2ggeWVhciBhZGRpbmcgdXAgdG8gMSwgdGh1cyBlYWNoIGJhciB3b3VsZCBoYXZlIHRoZSBzYW1lIGhlaWdodCAgICAgDQoNCldlIHdpbGwgdXNlIHRoZSBgZG9kZ2VgIG9wdGlvbi4NCg0KRm9yIHRoZSBjb2xvcnMsIHdlIGNhbiB0cnkgb3V0IHZhcmlvdXMgbnVtYmVycyBvZiBzaGFkZXMgZm9yIHRoZSBwYWxldHRlIHVudGlsIHdlIGdldCBzaGFkZXMgdGhhdCB3ZSBsaWtlLiBJbiB0aGlzIGNhc2UsIDIgc2hhZGVzIG91dCBvZiBhIGdyYWRpZW50IG9mIDQgc2hhZGVzIGZyb20gZ3JheSB0byB0ZWFsIGxvb2tzIG5pY2UsDQoNCmBgYHtyfSAgDQphc2lhbl9zdWJncm91cHMgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsIFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiKSAlPiUNCiAgbXV0YXRlKFllYXIgPSBhcy5jaGFyYWN0ZXIoWWVhcikpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBSYWNlX0V0aG5pY2l0eSwgeSA9IFBlcmNlbnQpKSArDQogICAgZ2VvbV9jb2woYWVzKGZpbGwgPSBZZWFyKSwgcG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgICBsYWJzKHRpdGxlID0gIllPVVRIIERJU0NPTk5FQ1RJT04gQlkgQVNJQU4gU1VCR1JPVVAsIDIwMTctMjAxOCIsDQogICAgICAgICB5ID0gIllPVVRIIERJU0NPTk5FQ1RJT04gKCUpIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWwpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiIzAwODM5MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpDQpgYGANCg0KT0ssIHRoaXMgbG9va3MgcHJldHR5IG5pY2UhIEhvd2V2ZXIsIHdlIGNhbiBpbXByb3ZlIHRoaXMgYSBiaXQuDQoNCkJlZm9yZSB3ZSBkbyBzbywgbGV0J3MgY3JlYXRlIGEgdGhlbWUgZm9yIG91ciBmdXR1cmUgc2ltaWxhciBwbG90cyBsaWtlIHNvOg0KDQpgYGB7cn0NCmJhcl90aGVtZSA8LSBmdW5jdGlvbigpIHsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMDA4MzkzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCiAgfQ0KYGBgDQoNCk5vdyB3ZSBzaW1wbHkgbmVlZCB0byB0eXBlIGBiYXJfdGhlbWUoKWAgaW5zdGVhZCB0byBhY2hpZXZlIHRoZSBzYW1lIHN0eWxlIGZvciBvdXIgcGxvdC4NCg0KSXQgd291bGQgYmUgbmljZSBpZiBmb3Igc3ViZ3JvdXBzIHRoYXQgb25seSBoYXZlIG9uZSB5ZWFyIG9mIGRhdGEsIGlmIHRoZSBjb2x1bW4gdGhhdCB3YXMgZGlzcGxheWVkIHdhcyBzdGlsbCB0aGUgc2FtZSB3aWR0aCBhcyB0aGUgdGhhdCBvZiB0aGUgb3RoZXIgZ3JvdXBzLiBUbyBkbyB0aGlzIHdlIG5lZWQgYSByb3cgd2l0aCBhbiBgTkFgIHZhbHVlIGZvciB0aGUgeWVhciB0aGF0IGlzIG1pc3NpbmcuIE9uZSB3YXkgdG8gYWNjb21wbGlzaCB0aGlzIGlzIHRvIHVzZSB0aGUgYHBpdm90X3dpZGVyKClgIGFuZCBgcGl2b3RfbG9uZ2VyKClgIGZ1bmN0aW9ucyB0byB3aWRlbiB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgYFllYXJgIHZhcmlhYmxlIGFuZCB0aGUgY29sbGFwc2UgdGhlIGRhdGEgYmFzZWQgb24gdGhlIGBZZWFyYCB2YXJpYWJsZS4gQnkgd2lkZW5pbmcgb3VyIGRhdGEsIHdlIGNyZWF0ZSB0aGUgYE5BYCB2YWx1ZXMgd2Ugd2FudDsgaG93ZXZlciB3ZSBuZWVkIHRvIGNvbGxhcHNlIG91ciBkYXRhIGJhY2sgdG8gdGhlIGxvbmcgZm9ybWF0IHNvIHRoYXQgd2UgY2FuIGVhc2lseSB1c2UgYGdncGxvdDJgIHRvIHBsb3QgdGhlIHllYXIgYXMgdGhlIGZpbGwgaW4gb3VyIGJhciBwbG90Lg0KDQpOb3RlIHRoYXQgd2Ugbm8gbG9uZ2VyIG5lZWQgdG8gY2hhbmdlIHRoZSB0eXBlIG9mIHRoZSBgWWVhcmAgdmFyaWFibGUsIGFzIGl0IGlzIGF1dG9tYXRpY2FsbHkgY29udmVydGVkIHRvIHR5cGUgY2hhcmFjdGVyLg0KDQpgYGB7cn0NCmFzaWFuX3N1Ymdyb3VwcyAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgIT0gIkFsbF9yYWNlcyIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IFBlcmNlbnQpDQpgYGANCg0KR3JlYXQhIE5vdyB3ZSBoYXZlIGBOQWAgdmFsdWVzLiBOb3cgd2UganVzdCB0byBnZXQgdGhlIGRhdGEgYmFjayBpbnRvIGxvbmcgZm9ybWF0Og0KDQpgYGB7cn0NCmFzaWFuX3N1Ymdyb3VwcyAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgIT0gIkFsbF9yYWNlcyIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IFBlcmNlbnQpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFJhY2VfRXRobmljaXR5LCBHZW5kZXIpLCANCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0iWWVhciIgLCANCiAgICAgICAgICAgICAgIHZhbHVlc190bz0iUGVyY2VudCIpDQpgYGANCg0KR3JlYXQsIG5vdyBsZXQncyBzZWUgaG93IHRoaXMgbG9va3MgaW4gdGhlIHBsb3QuDQoNCmBgYHtyfSAgDQphc2lhbl9zdWJncm91cHMgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsIFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtR3JvdXApICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gWWVhciwgdmFsdWVzX2Zyb20gPSBQZXJjZW50KSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhSYWNlX0V0aG5pY2l0eSwgR2VuZGVyKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9IlllYXIiICwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG89IlBlcmNlbnQiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUmFjZV9FdGhuaWNpdHksIHkgPSBQZXJjZW50KSkgKw0KICAgIGdlb21fY29sKGFlcyhmaWxsID0gWWVhciksIHBvc2l0aW9uID0gImRvZGdlIikrDQogICAgbGFicyh0aXRsZSA9ICJZT1VUSCBESVNDT05ORUNUSU9OIEJZIEFTSUFOIFNVQkdST1VQLCAyMDE3LTIwMTgiLA0KICAgICAgICAgeSA9ICJZT1VUSCBESVNDT05ORUNUSU9OICglKSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsKSArDQogICAgYmFyX3RoZW1lKCkNCmBgYA0KDQpHcmVhdCEgdGhhdCBpcyBsb29raW5nIGJldHRlciEgSG93ZXZlciwgY3VycmVudGx5IHRoZSBzdWJncm91cHMgYXJlIHBsb3R0ZWQgb24gdGhlIHgtYXhpcyBieSBhbHBoYWJldGljYWxseSBvcmRlciBhbmQgd2Ugd2FudCB0byBpbnN0ZWFkIG9yZGVyIHRoZSBzdWJncm91cHMgYmFzZWQgb24gdGhlIHBlcmNlbnRhZ2Ugb2YgeW91dGggZGlzY29ubmVjdGlvbi4gV2UgY2FuIHVzZSB0aGUgYGZvcmNhdHNgIHBhY2thZ2UgdG8gZG8gdGhpcy4gDQoNClRoZSBgZmN0X3Jlb3JkZXIoKWAgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gb3JkZXIgdGhlIGBSYWNlX0V0aG5pY2l0eWAgdmFyaWFibGUgYmFzZWQgb24gdGhlIGF2ZXJhZ2Ugb2YgdGhlIGBQZXJjZW50YCB2YXJpYWJsZSBmb3IgdGhlIHR3byB5ZWFycywgd2hpbGUgdGhlIGBmY3RfcmVsZXZlbCgpYCBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBtYWtlIHRoZSBgQXNpYW5gIGxldmVsIGFwcGVhciBmaXJzdCBmb3IgY29tcGFyaXNvbiBzYWtlLiBJbXBvcnRhbnRseSwgd2UgbmVlZCB0byBkbyB0aGlzIGJlZm9yZSB3ZSByZXNoYXBlIHRoZSBkYXRhLCBiZWNhdXNlIHN1Ymdyb3VwcyB3aXRoIGBOQWAgdmFsdWVzIHdpbGwgYmUgcGxhY2VkIGF0IHRoZSBlbmQuDQoNCmBgYHtyfQ0KYXNpYW5fc3ViZ3JvdXBzICU+JQ0KICBmaWx0ZXIoR2VuZGVyID09ICJBbGwiLCBSYWNlX0V0aG5pY2l0eSAhPSAiQWxsX3JhY2VzIikgJT4lDQogIG11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IGZjdF9yZW9yZGVyKFJhY2VfRXRobmljaXR5LCBQZXJjZW50KSwgDQogICAgICAgICBSYWNlX0V0aG5pY2l0eSA9IGZjdF9yZWxldmVsKFJhY2VfRXRobmljaXR5LCAiQXNpYW4iKSkgJT4lIA0KICBzZWxlY3QoLUdyb3VwKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gUGVyY2VudCkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUmFjZV9FdGhuaWNpdHksIEdlbmRlciksIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSJZZWFyIiwgdmFsdWVzX3RvPSJQZXJjZW50IiklPiUNCiAgZ2dwbG90KGFlcyh4ID0gUmFjZV9FdGhuaWNpdHksIHkgPSBQZXJjZW50KSkgKw0KICAgIGdlb21fY29sKGFlcyhmaWxsID0gWWVhciksIHBvc2l0aW9uID0gImRvZGdlIikrDQogICAgbGFicyh0aXRsZSA9ICJZT1VUSCBESVNDT05ORUNUSU9OIEJZIEFTSUFOIFNVQkdST1VQIiwNCiAgICAgICAgIHN1YnRpdGxlID0gIk9SREVSRUQgQlkgQVZFUkFHRSBESVNDT05ORUNUSU9OIExFVkVMIE9GIDIwMTcgJiAyMDE4IiwNCiAgICAgICAgIHkgPSAiWU9VVEggRElTQ09OTkVDVElPTiAoJSkiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX3BhbCkgKw0KICAgIGJhcl90aGVtZSgpDQpgYGANClRoaXMgaXMgbG9va2luZyB2ZXJ5IGdvb2QhIE5vdywgbGV0J3MgbWFrZSBhIGdhcCBiZXR3ZWVuIHRoZSBBc2lhbiBhdmVyYWdlIGdyb3VwIGFuZCB0aGUgc3ViZ3JvdXBzIHRvIG1vcmUgZWFzaWx5IGRpZmZlcmVudGlhdGUgYmV0d2VlbiB0aGUgdHdvIGFuZCBsZXQncyB1c2UgdGhhdCBzcGFjZSB0byBhZGQgdGhlIGZpZ3VyZSBsZWdlbmQgdG8gdGhlIHBsb3QgaXRzZWxmLg0KDQpUbyBmaXJzdCBtYWtlIGEgZ2FwLCB3ZSBjYW4gdXNlIHRoZSBgc2NhbGVfeF9kaXNjcmV0ZSgpYCBmdW5jdGlvbi4gV2UgbmVlZCB0byB3cml0ZSB0aGUgbmFtZSBvZiB0aGUgbGV2ZWxzIG9mIHRoZSBgUmFjZV9FdGhuaWNpdHlgIHZhcmlhYmxlIGFuZCBwbGFjZSBhIGJsYW5rIGluIGJldHdlZW4gdGhlIGBBc2lhbmAgbGV2ZWwgYW5kIHRoZSBzdWJzZXF1ZW50IGxldmVscy4gV2UgY2FuIHVzZSB0aGUgYGxldmVscygpYCBmdW5jdGlvbiB0byBtYWtlIHRoaXMgbW9yZSByZXByb2R1Y2libGUgYW5kIHRvIGF2b2lkIG1pc3Rha2VzIGluc3RlYWQgb2YgYWN0dWFsbHkgd3JpdGluZyBvdXQgZWFjaCBvZiB0aGUgbGV2ZWxzIGJ5IGhhbmQuDQoNCkhlcmUgeW91IGNhbiBzZWUgYWxsIHRoZSBsZXZlbHM6DQoNCmBgYHtyfQ0KYXNpYW5fc3ViZ3JvdXBzICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsIFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiKSAlPiUNCiAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gIGZjdF9yZW9yZGVyKFJhY2VfRXRobmljaXR5LCBQZXJjZW50KSwNCiAgICAgICAgIFJhY2VfRXRobmljaXR5ID0gZmN0X3JlbGV2ZWwoUmFjZV9FdGhuaWNpdHksICJBc2lhbiIpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gUGVyY2VudCkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUmFjZV9FdGhuaWNpdHksIEdlbmRlciksIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiWWVhciIsIHZhbHVlc190byA9ICJQZXJjZW50IiklPiUNCiAgcHVsbChSYWNlX0V0aG5pY2l0eSkgJT4lDQogIGxldmVscygpDQpgYGANClRvIHNpbXBsaWZ5IG91ciBjb2RlIHdlIHdpbGwgc2F2ZSBvdXIgd3JhbmdsZWQgZGF0YSBhcyBhIHRpYmJsZSBjYWxsZWQgYGRmX2FzaWFuX3N1Ymdyb3VwYC4NCg0KU28gd2Ugd291bGQgbGlrZSBvdXIgeC1heGlzIHRvIGJlIGxpa2UgdGhpczoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQpzY2FsZV94X2Rpc2NyZXRlKA0KICBsaW1pdHMgPSBjKCJBc2lhbiIsDQogICAgICAgICAgICAgIkJsYW5rIiwNCiAgICAgICAgICAgICAiTkVYVF9ibGFuayIsDQogICAgICAgICAgICAgbGV2ZWxzKHB1bGwoZGZfYXNpYW5fc3ViZ3JvdXAsIFJhY2VfRXRobmljaXR5KSlbMjoxMF0pLA0KICBsYWJlbHMgPSBjKCJCbGFuayIgPSAiIiwNCiAgICAgICAgICAgICAiTkVYVF9ibGFuayI9ICIiKSkNCmBgYA0KDQpUaGUgYGxldmVscyhwdWxsKGRmX2FzaWFuX3N1Ymdyb3VwLCBSYWNlX0V0aG5pY2l0eSkpWzI6MTBdKWAgY29kZSBnZXRzIGFsbCA5IG90aGVyIGxldmVscyBidXQgdGhlIGBBc2lhbmAgbGV2ZWwgb2YgdGhlIGBSYWNlX0V0aG5pY2l0eWAgdmFyaWFibGUuIFRoZSBgbGFiZWxzYCBhcmd1bWVudCBzcGVjaWZpZXMgd2hhdCB0aGUgeC1heGlzIHNob3VsZCBzYXkgZm9yIHRoZSBzcGFjZXMgaW4gYmV0d2Vlbi4gV2Ugd291bGQgbGlrZSB0aGVtIHRvIHNheSBub3RoaW5nLCBzbyB3ZSB1c2Ugbm90aGluZyB3aXRoaW4gcXVvdGVzIGAiImAgYXMgdGhlIGxhYmVsLiANCg0KV2UgY2FuIGFsc28gdXNlIHRoaXMgYXMgYW4gb3Bwb3J0dW5pdHkgdG8gY2hhbmdlIHRoZSBsYWJlbCBvbiB0aGUgcGxvdCBmb3IgdGhlIGBBc2lhbmAgbGV2ZWwgb2YgdGhlIGBSYWNlX0V0aG5pY2l0eWAgdmFyaWFibGUgdG8gYmUgYCJBc2lhbiBhdmcuImAuIFdlIGRvbid0IHdhbnQgdG8gY2hhbmdlIHRoZSBvdGhlciBzdWJncm91cCBsZXZlbCBsYWJlbHMgc28gd2Ugd2lsbCBub3QgbGlzdCBhbnl0aGluZy4NCg0KV2UgY2FuIG1ha2UgdGhpcyBtb3JlIHJlcHJvZHVjaWJsZSBieSB1c2luZyB0aGUgYGxlbmd0aCgpYCBmdW5jdGlvbiBpbnN0ZWFkIG9mIHRoZSBudW1iZXIgMTAgYW5kIHJlcGxhY2luZyBgQXNpYW5gIHdpdGggdGhlIGZpcnN0IGxldmVsOg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0Kc2NhbGVfeF9kaXNjcmV0ZSgNCiAgbGltaXRzID0gYyhsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCwgUmFjZV9FdGhuaWNpdHkpKVsxXSwNCiAgICAgICAgICAgICAiQmxhbmsiLA0KICAgICAgICAgICAgICJORVhUX2JsYW5rIiwNCiAgICAgICAgICAgICBsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCwgUmFjZV9FdGhuaWNpdHkpKVsyOg0KICAgICAgICAgICAgIGxlbmd0aChsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCwgUmFjZV9FdGhuaWNpdHkpKSldKSwNCiAgbGFiZWxzID0gYygiQXNpYW4iID0gIkFzaWFuIEF2Zy4iLA0KICAgICAgICAgICAgICJCbGFuayIgPSAiIiwNCiAgICAgICAgICAgICAiTkVYVF9ibGFuayI9ICIiKSkNCmBgYA0KDQpUaGVuIHdlIGNhbiB1c2UgYGxlZ2VuZC5qdXN0aWZpY2F0aW9uYCBhbmQgYGxlZ2VuZC5wb3NpdGlvbmAgb2YgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiB0byBtb3ZlIG91ciBsZWdlbmQgdG8gdGhlIHBsb3QgYXJlYS4gDQoNCkFjY29yZGluZyB0byB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2UsIHRoZSBgbGVnZW5kLmp1c3RpZmljYXRpb25gIGFyZ3VtZW50IHNwZWNpZmllcyB0aGU6DQoNCj4gYW5jaG9yIHBvaW50IGZvciBwb3NpdGlvbmluZyBsZWdlbmQgaW5zaWRlIHBsb3QgKCJjZW50ZXIiIG9yIHR3by1lbGVtZW50IG51bWVyaWMgdmVjdG9yKSBvciB0aGUganVzdGlmaWNhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIHBsb3QgYXJlYSB3aGVuIHBvc2l0aW9uZWQgb3V0c2lkZSB0aGUgcGxvdA0KDQpCeSBzZXR0aW5nIHRoZSBtYXBwaW5nIGZvciB0aGUgcG9zaXRpb25pbmcgYW5kIGp1c3RpZmljYXRpb24gYXMgYGMoMC4xMiwwLjEpYCB3ZSBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0aGUgbGVnZW5kIHRvIGJlIDEyIHBlcmNlbnQgb2YgdGhlIHBsb3QgYXJlYSBmcm9tIHRoZSB5LWF4aXMgYW5kIDEwIHBlcmNlbnQgb2YgdGhlIHBsb3QgYXJlYSBmcm9tIHRoZSB4LWF4aXMuDQoNCmBgYHtyfQ0KZGZfYXNpYW5fc3ViZ3JvdXAgPC0NCiAgYXNpYW5fc3ViZ3JvdXBzICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsIA0KICAgICAgICAgUmFjZV9FdGhuaWNpdHkgIT0gIkFsbF9yYWNlcyIpICU+JQ0KICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSAgZmN0X3Jlb3JkZXIoUmFjZV9FdGhuaWNpdHksIFBlcmNlbnQpLCANCiAgICAgICAgIFJhY2VfRXRobmljaXR5ID0gZmN0X3JlbGV2ZWwoUmFjZV9FdGhuaWNpdHksICJBc2lhbiIpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gUGVyY2VudCkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUmFjZV9FdGhuaWNpdHksIEdlbmRlciksIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiWWVhciIsIHZhbHVlc190byA9ICJQZXJjZW50IikNCg0KYXNpYW5fc3ViZ3JvdXBfcGxvdCA8LQ0KICBkZl9hc2lhbl9zdWJncm91cCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUmFjZV9FdGhuaWNpdHksIHkgPSBQZXJjZW50LCBmaWxsID0gWWVhcikpICsNCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IFllYXIpLCBwb3NpdGlvbiA9ICJkb2RnZSIpKw0KICAgIGxhYnModGl0bGUgPSAiWU9VVEggRElTQ09OTkVDVElPTiBCWSBBU0lBTiBTVUJHUk9VUCIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJPUkRFUkVEIEJZIEFWRVJBR0UgRElTQ09OTkVDVElPTiBMRVZFTCBPRiAyMDE3ICYgMjAxOCIsDQogICAgICAgICB5ID0gIllPVVRIIERJU0NPTk5FQ1RJT04gKCUpIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsKSArDQogIHNjYWxlX3hfZGlzY3JldGUoDQogICAgbGltaXRzID0gYyhsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCxSYWNlX0V0aG5pY2l0eSkpWzFdLA0KICAgICAgICAgICAgICJCbGFuayIsDQogICAgICAgICAgICAgIk5FWFRfYmxhbmsiLA0KICAgICAgICAgICAgIGxldmVscyhwdWxsKGRmX2FzaWFuX3N1Ymdyb3VwLFJhY2VfRXRobmljaXR5KSlbMjoNCiAgICAgICAgICAgICBsZW5ndGgobGV2ZWxzKHB1bGwoZGZfYXNpYW5fc3ViZ3JvdXAsUmFjZV9FdGhuaWNpdHkpKSldKSwNCiAgICBsYWJlbHMgPSBjKCJBc2lhbiIgPSAiQXNpYW4gQXZnLiIsICJCbGFuayIgPSAiIiwgIk5FWFRfYmxhbmsiPSAiIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCxtYXgocHVsbChhc2lhbl9zdWJncm91cHMsUGVyY2VudCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkpKw0KICBiYXJfdGhlbWUoKSArDQogICMgdGhpcyBhZGQgdGhlIGxlZ2VuZCB0byB0aGUgcGxvdCBhcmVhIToNCiAgICAgICAgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAuMTIsMC4xKSwNCiAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjEyLC4xKSkNCg0KYXNpYW5fc3ViZ3JvdXBfcGxvdA0KYGBgDQoNCk5pY2UhIEJ1dCwgdGhpcyBpcyBzdGlsbCBtaXNzaW5nIHNvbWV0aGluZy4gUGVyaGFwcyBpZiB3ZSBhZGQgbGluZXMgdGhhdCBzaG93IHRoZSBhdmVyYWdlIGRpc2Nvbm5lY3Rpb24gcmF0ZSBmb3IgdGhlIFVTIGFuZCBmb3IgQXNpYW5zIGluIGdlbmVyYWwsIHRoZW4gaXQgd2lsbCBtYWtlIGl0IGVhc2llciB0byBzZWUgZGlmZmVyZW5jZXMgaW4gb3VyIHBsb3QuDQoNCkluIHRoZSBwcmV2aW91cyBjb2RlIHdlIHNhdmVkIG91ciBwbG90IHRvIGEgb2JqZWN0IGNhbGxlZCBgYXNpYW5fc3ViZ3JvdXBfcGxvdGAuDQoNCldlIGNhbiBtb2RpZnkgaXQgYnkgYWRkaW5nIGxheWVycyB0byBhZGQgdGhlIGxpbmVzIHRoYXQgd2Ugd2FudC4NCg0KRmlyc3QsIHdlIHdhbnQgdG8gY3JlYXRlIHRpYmJsZXMgZm9yIHRoZSBVUyBBc2lhbiBhdmVyYWdlIGRpc2Nvbm5lY3Rpb24gcmF0ZXMuIFdlIHdhbnQgdG8gYWRkIGxhYmVscyB0byBlYWNoIG9mIHRoZXNlLiBXZSBjYW4gdXNlIHRoZSBgcGFyc2UgPSBUUlVFYCBhcmd1bWVudCBsYXRlciB3aXRoIHRoZSBgZ2VvbV90ZXh0KClgIGZ1bmN0aW9uIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZSB0byBtYWtlIHRoZXNlIGxhYmVscyBhcHBlYXIgYXMgYm9sZCBmb250IGFzIGl0IHdpbGwgYmUgZXZhbHVhdGVkIGFzIGFuIGV4cHJlc3Npb24gaW5zdGVhZCBvZiBhIHNpbXBsZSBjaGFyYWN0ZXIgc3RyaW5nLg0KDQpgYGB7cn0NCmRmX2FzaWFuX3RvdGFsIDwtIA0KICBhc2lhbl9zdWJncm91cHMgJT4lDQogIGZpbHRlcihZZWFyID09IDIwMTcsIEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgPT0gIkFzaWFuIikgJT4lDQogIG11dGF0ZShZZWFyID0gYXMuY2hhcmFjdGVyKFllYXIpLCANCiAgICAgICAgIGxhYmVsID0gYygnYm9sZCgiQVNJQU4gMjAxNyBBVkcgUkFURSIpJykpDQoNCmRmX1VTX3RvdGFsIDwtIA0KICBhc2lhbl9zdWJncm91cHMgJT4lDQogIGZpbHRlcihZZWFyID09IDIwMTcsIEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgPT0gIkFsbF9yYWNlcyIpICU+JQ0KICBtdXRhdGUoWWVhciA9IGFzLmNoYXJhY3RlcihZZWFyKSwgbGFiZWwgPSBjKCdib2xkKCJVUyAyMDE3IEFWRyBSQVRFIiknKSkNCg0KZGZfYXNpYW5fdG90YWwNCmRmX1VTX3RvdGFsDQpgYGANCg0KTm93IHdlIGNhbiB1c2UgdGhlc2UgdG8gY3JlYXRlIGxhYmVscyBhZ2FpbiB3aXRoIHRoZSBgZ2VvbV90ZXh0KClgIGZ1bmN0aW9uIGFuZCBsaW5lcyB3aXRoIHRoZSBgZ2VvbV9obGluZSgpYCBmdW5jdGlvbiBvbiBvdXIgcGxvdC4gDQoNClRoZSBgZ2VvbV90ZXh0KClgIGZ1bmN0aW9uIHJlcXVpcmVzIHRoYXQgdGhlIHggYW5kIHkgYXhpcyBsb2NhdGlvbiBmb3IgdGhlIHRleHQgYmUgc3BlY2lmaWVkLCBhbmQgd2Ugd2FudCB0aGlzIHRvIGJlIGxvY2F0ZWQgbmVhciB0aGUgYWN0dWFsIHBlcmNlbnQgZGlzY29ubmVjdGlvbiByYXRlLCB0aHVzIHdlIHVzZSB0aGUgYFBlcmNlbnQgKy41YCB0byBncmFiIHRoZSBgUGVyY2VudGAgdmFsdWUgZnJvbSBgZGZfYXNpYW5fdG90YWxgIGFuZCBhZGQgMC41IHRvIGl0IHRvIGJlIHNsaWdodGx5IGFib3ZlIHdoZXJlIHRoZSBsaW5lIHdpbGwgYmUuIA0KDQpUaGUgYGdlb21faGxpbmUoKWAgZnVuY3Rpb24gcmVxdWlyZXMsIGp1c3QgdGhlIHkgaW50ZXJjZXB0IGFzIHRoaXMgZnVuY3Rpb24gYWx3YXlzIGNyZWF0ZXMgYSBob3Jpem9udGFsIGxpbmUuIFRoZSBgbGluZXR5cGVgIGFyZ3VtZW50IHNwZWNpZmllcyB3aGF0IHN0eWxlIG9mIGxpbmUgd2Ugd291bGQgbGlrZS4gU2VlIFtoZXJlXShodHRwOi8vc2FwZS5pbmYudXNpLmNoL3F1aWNrLXJlZmVyZW5jZS9nZ3Bsb3QyL2xpbmV0eXBlKSBmb3IgYSBsaXN0IG9mIG9wdGlvbnMuDQoNCmBgYHtyfQ0KYXNpYW5fc3ViZ3JvdXBfcGxvdCA8LSANCiAgYXNpYW5fc3ViZ3JvdXBfcGxvdCArIA0KICBnZW9tX3RleHQoZGF0YSA9IGRmX2FzaWFuX3RvdGFsLCANCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IDIuNSwgeSA9IFBlcmNlbnQgKy43LCBsYWJlbCA9IGxhYmVsKSwNCiAgICAgICAgICAgIHBhcnNlID0gVFJVRSwNCiAgICAgICAgICAgIGNvbG9yID0gIiMwMDgzOTMiLCANCiAgICAgICAgICAgIHNpemUgPSA0KSArDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBwdWxsKGRmX2FzaWFuX3RvdGFsLCBQZXJjZW50KSksIGxpbmV0eXBlID0gMikgKw0KICBnZW9tX3RleHQoZGF0YSA9IGRmX1VTX3RvdGFsLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gMi41LCB5ID0gUGVyY2VudCArLjcsIGxhYmVsID0gbGFiZWwpLA0KICAgICAgICAgICAgcGFyc2UgPSBUUlVFLA0KICAgICAgICAgICAgY29sb3IgPSAiIzAwODM5MyIsIA0KICAgICAgICAgICAgc2l6ZSA9IDQpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IHB1bGwoZGZfVVNfdG90YWwsIFBlcmNlbnQpKSwgbGluZXR5cGUgPSAzKQ0KDQphc2lhbl9zdWJncm91cF9wbG90DQpgYGANCg0KQXdlc29tZSEgTm93IGl0IGlzIG11Y2ggZWFzaWVyIHRvIHRlbGwgaG93IHRoZSBkaXNjb25uZWN0aW9uIHJhdGVzIGZvciB2YXJpb3VzIHN1Ymdyb3VwcyBjb21wYXJlIHRvIHRoZSBuYXRpb25hbCBhdmVyYWdlIGFuZCB0aGUgQXNpYW4gYXZlcmFnZS4gRnJvbSBvdXIgcGxvdCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBbSG1vbmddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0htb25nX3Blb3BsZSkgYW5kIENhbWJvZGlhbiBzdWJncm91cCByYXRlcyBhcmUgdmVyeSBoaWdoLiBJZiB3ZSBvbmx5IGhhZCB0aGUgcGxvdCBvZiBtYWpvciByYWNpYWwgYW5kIGV0aG5pYyBncm91cHMgdG8gcmVseSBvbiwgd2UgbWlnaHQgbm90IHJlYWxpemUgdGhhdCB0aGVzZSB0d28gZ3JvdXBzIGhhdmUgc3VjaCBoaWdoIHJhdGVzLiANCg0KTmljZSEgVGhpcyBzaG93cyB0aGUgaW1wb3J0YW5jZSBvZiBhZGRpbmcgc21hbGwgZGV0YWlscyBzdWNoIGFzIHRoZSBVUyBhbmQgQXNpYW4gcmF0ZSBsaW5lcy4gVGhpcyBoZWxwcyBwcm92aWRlIGEgc2ltcGxlIHlldCBudWFuY2VkIHBpY3R1cmUgb2Ygd2hhdCBpcyBnb2luZyBvbi4gDQoNCkZyb20gdGhlIGFib3ZlIHBsb3QsIGl0IGJlY29tZXMgcmVhZGlseSBhcHBhcmVudCB0aGF0IHRoZSBIbW9uZyBhbmQgQ2FtYm9kaWFuIHN1Ymdyb3VwcyBoYXZlIG11Y2ggaGlnaGVyIGRpc2Nvbm5lY3Rpb24gcmF0ZXMgdGhhbiB0aGUgb3RoZXIgQXNpYW4gc3ViZ3JvdXBzLCBhcyB3ZWxsIGFzIGFsbCByYWNlcyAvIGV0aG5pY2l0aWVzIGluIHRoZSBVUyBjb21iaW5lZC4gDQoNCklmIHdlIHdhbnRlZCB0byBtYWtlIGEgc2ltaWxhciBwbG90IGJ1dCB3aXRob3V0IHRoZSBzdWJncm91cHMgdGhhdCBhcmUgbWlzc2luZyBhIHllYXIgd2UgY291bGQgZG8gaXQgbGlrZSBzbywgYnkgYWRkaW5nIHRoZSBgZHJvcF9uYSgpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIHRvIHJlbW92ZSByb3dzIHdpdGggYE5BYCB2YWx1ZXMgd2hlbiB0aGUgZGF0YSAgaXMgaW4gd2lkZSAgZm9ybSBhbmQgdGhlbiB1c2luZyB0aGUgYGZjdF9kcm9wKClgIGZ1bmN0aW9uIG9mIHRoZSBgZm9yY2F0c2AgcGFja2FnZSB0byByZW1vdmUgdGhlIGxldmVscyBvZiB0aGUgYFJhY2VfRXRobmljaXR5YCB2YXJpYWJsZSB0aGF0IG5vIGxvbmdlciBoYXZlIHJvd3MgaW4gdGhlIGRhdGEuIA0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+IA0KYGBge3J9DQpkZl9hc2lhbl9zdWJncm91cCA8LQ0KICBhc2lhbl9zdWJncm91cHMgJT4lDQogIGRwbHlyOjpzZWxlY3QoLUdyb3VwKSAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgIT0gIkFsbF9yYWNlcyIpICU+JQ0KICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSAgZmN0X3Jlb3JkZXIoUmFjZV9FdGhuaWNpdHksIFBlcmNlbnQpLCANCiAgICAgICAgIFJhY2VfRXRobmljaXR5ID0gZmN0X3JlbGV2ZWwoUmFjZV9FdGhuaWNpdHksICJBc2lhbiIpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gUGVyY2VudCkgJT4lDQogIHRpZHlyOjpkcm9wX25hKCkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUmFjZV9FdGhuaWNpdHksIEdlbmRlciksIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiWWVhciIsIHZhbHVlc190byA9ICJQZXJjZW50IikgJT4lDQogIG11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IGZjdF9kcm9wKFJhY2VfRXRobmljaXR5KSkNCg0KYXNpYW5fc3ViZ3JvdXBfcGxvdF9zaW1wIDwtDQogIGRmX2FzaWFuX3N1Ymdyb3VwICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBSYWNlX0V0aG5pY2l0eSwgeSA9IFBlcmNlbnQsIGZpbGwgPSBZZWFyKSkgKw0KICAgIGdlb21fY29sKGFlcyhmaWxsID0gWWVhciksIHBvc2l0aW9uID0gImRvZGdlIikrDQogICAgbGFicyh0aXRsZSA9ICJZT1VUSCBESVNDT05ORUNUSU9OIEJZIEFTSUFOIFNVQkdST1VQIiwNCiAgICAgICAgIHN1YnRpdGxlID0gIk9SREVSRUQgQlkgQVZFUkFHRSBESVNDT05ORUNUSU9OIExFVkVMIE9GIDIwMTcgJiAyMDE4IiwNCiAgICAgICAgIHkgPSAiWU9VVEggRElTQ09OTkVDVElPTiAoJSkiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX3BhbCkgKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoDQogICAgbGltaXRzID0gYyhsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCxSYWNlX0V0aG5pY2l0eSkpWzFdLA0KICAgICAgICAgICAgICAgIkJsYW5rIiwNCiAgICAgICAgICAgICAgICJORVhUX2JsYW5rIiwNCiAgICAgICAgICAgICAgIGxldmVscyhwdWxsKGRmX2FzaWFuX3N1Ymdyb3VwLFJhY2VfRXRobmljaXR5KSlbMjoNCiAgICAgICAgICAgICAgIGxlbmd0aChsZXZlbHMocHVsbChkZl9hc2lhbl9zdWJncm91cCxSYWNlX0V0aG5pY2l0eSkpKV0pLA0KICAgIGxhYmVscyA9IGMoIkFzaWFuIiA9ICJBc2lhbiBBdmcuIiwNCiAgICAgICAgICAgICAgICJCbGFuayIgPSAiIiwNCiAgICAgICAgICAgICAgICJORVhUX2JsYW5rIj0gIiIpKSsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLG1heChwdWxsKGFzaWFuX3N1Ymdyb3VwcyxQZXJjZW50KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkpKw0KICAgIGJhcl90aGVtZSgpICsNCiAgICAjdGhpcyBhZGQgdGhlIGxlZ2VuZCB0byB0aGUgcGxvdCBhcmVhIToNCiAgICB0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMC4xNiwwLjAxKSxsZWdlbmQucG9zaXRpb24gPSBjKDAuMTYsLjAxKSkgKw0KICAgIGdlb21fdGV4dChkYXRhID0gZGZfYXNpYW5fdG90YWwsDQogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IDIuNSwgeSA9IFBlcmNlbnQgKy43LCBsYWJlbCA9IGxhYmVsKSwNCiAgICAgICAgICAgICAgcGFyc2UgPSBUUlVFLCBjb2xvciA9ICIjMDA4MzkzIiwgc2l6ZSA9IDQpICsNCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gcHVsbChkZl9hc2lhbl90b3RhbCwgUGVyY2VudCkpLCBsaW5ldHlwZSA9IDIpICsNCiAgICBnZW9tX3RleHQoZGF0YSA9IGRmX1VTX3RvdGFsLA0KICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSAyLjUsIHkgPSBQZXJjZW50ICsuNywgbGFiZWwgPSBsYWJlbCksDQogICAgICAgICAgICAgIHBhcnNlID0gVFJVRSwgY29sb3IgPSAiIzAwODM5MyIsIHNpemUgPSA0KSArDQogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IHB1bGwoZGZfVVNfdG90YWwsIFBlcmNlbnQpKSwgbGluZXR5cGUgPSAzKQ0KYGBgDQo8L2RldGFpbHM+IA0KDQpgYGB7cn0NCmFzaWFuX3N1Ymdyb3VwX3Bsb3Rfc2ltcA0KYGBgDQoNCioqKg0KDQpOb3cgbGV0J3MgZG8gdGhlIHNhbWUgZm9yIHRoZSBMYXRpbnggc3ViZ3JvdXBzLg0KDQpGaXJzdCBsZXQncyBtYWtlIHRoZSBzbWFsbCB0aWJibGUgb2YgdGhlIDIwMTcgTGF0aW54IGF2ZXJhZ2UgcmF0ZSBsaWtlIHdlIGRpZCBmb3IgdGhlIFVTIGFuZCBmb3IgQXNpYW5zIHRvIGNyZWF0ZSB0aGUgbGluZXMgYW5kIHRleHQgb24gb3VyIHBsb3QuDQoNCmBgYHtyfQ0KZGZfbGF0aW54X3RvdGFsIDwtIA0KICBsYXRpbnhfc3ViZ3JvdXBzICU+JQ0KICBmaWx0ZXIoWWVhciA9PSAyMDE3LCBHZW5kZXIgPT0gIkFsbCIsIFJhY2VfRXRobmljaXR5ID09ICJMYXRpbngiKSAlPiUNCiAgbXV0YXRlKFllYXIgPSBhcy5jaGFyYWN0ZXIoWWVhciksIA0KICAgICAgICAgbGFiZWwgPSBjKCdib2xkKCJMQVRJTlggMjAxNyBBVkcgUkFURSIpJykpDQpgYGANCg0KDQpOb3cgd2Ugd2lsbCB1c2Ugc2ltaWxhciBjb2RlIGZvciB0aGUgbGF0aW54IHN1Ymdyb3Vwcy4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IHdlIHdpbGwgYWxzbyBjaGFuZ2UgdGhlIGAiUHVlcnRvIFJpY2FuLCBEb21pbmljYW4sIEN1YmFuImAgdmFsdWVzIHRvIGhhdmUgbmV3IGxpbmUgYnJlYWtzIGJldHdlZW4gdGhlIGdyb3Vwcy4gRmlyc3QgbGV0J3MgcGxvdCBhbGwgc3ViZ3JvdXBzLg0KDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gcmV2ZWFsIHRoZSBjb2RlLiA8L3N1bW1hcnk+IA0KYGBge3J9DQpsYXRpbnhfZm9yX3Bsb3QgPC1sYXRpbnhfc3ViZ3JvdXBzICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsIFJhY2VfRXRobmljaXR5ICE9ICJBbGxfcmFjZXMiKSAlPiUNCiAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gc3RyX3JlcGxhY2UoDQogICAgICAgICAgICAgICAgIHN0cmluZyA9IFJhY2VfRXRobmljaXR5LCANCiAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIlB1ZXJ0byBSaWNhbiwgRG9taW5pY2FuLCBDdWJhbiIsDQogICAgICAgICAgICByZXBsYWNlbWVudCA9ICJQdWVydG8gUmljYW4sXG5Eb21pbmljYW4sXG5DdWJhbiIpKSAlPiUNCiAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gZmN0X3Jlb3JkZXIoUmFjZV9FdGhuaWNpdHksIFBlcmNlbnQpLCANCiAgICAgICAgIFJhY2VfRXRobmljaXR5ID0gZmN0X3JlbGV2ZWwoUmFjZV9FdGhuaWNpdHksICJMYXRpbngiKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IFBlcmNlbnQpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFJhY2VfRXRobmljaXR5LCBHZW5kZXIpLCANCiAgbmFtZXNfdG8gPSAiWWVhciIgLCANCiAgdmFsdWVzX3RvID0gIlBlcmNlbnQiKQ0KDQpsYXRpbnhfc3ViZ3JvdXBfcGxvdCA8LSANCiAgbGF0aW54X2Zvcl9wbG90ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBSYWNlX0V0aG5pY2l0eSwgeSA9IFBlcmNlbnQsIGZpbGwgPSBZZWFyKSkgKw0KICAgIGdlb21fY29sKGFlcyhmaWxsID0gWWVhciksIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICAgIGxhYnModGl0bGUgPSAiWU9VVEggRElTQ09OTkVDVElPTiBCWSBMQVRJTlggU1VCR1JPVVAiLA0KICAgICAgICAgc3VidGl0bGUgPSAiT1JERVJFRCBCWSBBVkVSQUdFIERJU0NPTk5FQ1RJT04gTEVWRUwgT0YgMjAxNyAmIDIwMTgiLA0KICAgICAgICAgeSA9ICJZT1VUSCBESVNDT05ORUNUSU9OICglKSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsKSArDQogICAgc2NhbGVfeF9kaXNjcmV0ZSgNCiAgICBsaW1pdHMgPSBjKGxldmVscyhwdWxsKGxhdGlueF9mb3JfcGxvdCxSYWNlX0V0aG5pY2l0eSkpWzFdLA0KICAgICAgICAgICAgICAgIkJsYW5rIiwNCiAgICAgICAgICAgICAgICJORVhUX2JsYW5rIiwNCiAgICAgICAgICAgICAgIGxldmVscyhwdWxsKGxhdGlueF9mb3JfcGxvdCxSYWNlX0V0aG5pY2l0eSkpWzI6DQogICAgICAgICAgICAgICBsZW5ndGgobGV2ZWxzKHB1bGwobGF0aW54X2Zvcl9wbG90LFJhY2VfRXRobmljaXR5KSkpXSksDQogICAgbGFiZWxzID0gYygiTGF0aW54IiA9ICJMYXRpbnggQXZnLiIsDQogICAgICAgICAgICAgICAiQmxhbmsiID0gIiIsDQogICAgICAgICAgICAgICAiTkVYVF9ibGFuayI9ICIiKSkrDQogICAgYmFyX3RoZW1lKCkgKw0KICAgICN0aGlzIGFkZCB0aGUgbGVnZW5kIHRvIHRoZSBwbG90IGFyZWEhOg0KICAgIHRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLjIzLDAuMSksbGVnZW5kLnBvc2l0aW9uID0gYygwLjIzLC4xKSkrDQogICAgZ2VvbV90ZXh0KGRhdGEgPSBkZl9sYXRpbnhfdG90YWwsDQogICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IDMsIHkgPSBQZXJjZW50ICsuNywgbGFiZWwgPSBsYWJlbCksDQogICAgICAgICAgICAgcGFyc2UgPSBUUlVFLCBjb2xvciA9ICIjMDA4MzkzIiwgc2l6ZSA9IDQpICsNCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gcHVsbChkZl9sYXRpbnhfdG90YWwsIFBlcmNlbnQpKSwgbGluZXR5cGUgPSAyKSArDQogICAgZ2VvbV90ZXh0KGRhdGEgPSBkZl9VU190b3RhbCwNCiAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gMywgeSA9IFBlcmNlbnQgKy43LCBsYWJlbCA9IGxhYmVsKSwNCiAgICAgICAgICAgICBwYXJzZSA9IFRSVUUsIGNvbG9yID0gIiMwMDgzOTMiLCBzaXplID0gNCkgKw0KICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBwdWxsKGRmX1VTX3RvdGFsLCBQZXJjZW50KSksIGxpbmV0eXBlID0gMykNCmBgYA0KPC9kZXRhaWxzPiANCg0KYGBge3J9DQpsYXRpbnhfc3ViZ3JvdXBfcGxvdA0KYGBgDQoNCk5vdyBsZXQncyBkcm9wIHRoZSBzdWJncm91cHMgdGhhdCBkb24ndCBoYXZlIHZhbHVlcyBmb3IgYm90aCB5ZWFycy4NCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHJldmVhbCB0aGUgY29kZS4gPC9zdW1tYXJ5PiANCg0KYGBge3J9DQpsYXRpbnhfZm9yX3Bsb3QgPC1sYXRpbnhfc3ViZ3JvdXBzICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Hcm91cCkgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIpICU+JQ0KICBmaWx0ZXIoUmFjZV9FdGhuaWNpdHkgIT0gIkFsbF9yYWNlcyIpICU+JQ0KICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSBzdHJfcmVwbGFjZSgNCiAgICAgICAgICAgICAgICAgc3RyaW5nID0gUmFjZV9FdGhuaWNpdHksIA0KICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiUHVlcnRvIFJpY2FuLCBEb21pbmljYW4sIEN1YmFuIiwNCiAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIlB1ZXJ0byBSaWNhbixcbkRvbWluaWNhbixcbkN1YmFuIikpICU+JQ0KICBtdXRhdGUoUmFjZV9FdGhuaWNpdHkgPSAgZmN0X3Jlb3JkZXIoUmFjZV9FdGhuaWNpdHksIFBlcmNlbnQpKSAlPiUgDQogIG11dGF0ZShSYWNlX0V0aG5pY2l0eSA9IGZjdF9yZWxldmVsKFJhY2VfRXRobmljaXR5LCAiTGF0aW54IikpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gWWVhciwgdmFsdWVzX2Zyb20gPSBQZXJjZW50KSAlPiUNCiAgdGlkeXI6OmRyb3BfbmEoKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhSYWNlX0V0aG5pY2l0eSwgR2VuZGVyKSwgDQogIG5hbWVzX3RvID0gIlllYXIiICwgDQogIHZhbHVlc190byA9ICJQZXJjZW50IiklPiUNCiAgbXV0YXRlKFJhY2VfRXRobmljaXR5ID0gZmN0X2Ryb3AoUmFjZV9FdGhuaWNpdHkpKQ0KDQpsYXRpbnhfc3ViZ3JvdXBfcGxvdF9zaW1wIDwtbGF0aW54X2Zvcl9wbG90ICU+JQ0KZ2dwbG90KGFlcyh4ID0gUmFjZV9FdGhuaWNpdHksIHkgPSBQZXJjZW50LCBmaWxsID0gWWVhcikpICsNCiBnZW9tX2NvbChhZXMoZmlsbCA9IFllYXIpLCBwb3NpdGlvbiA9ICJkb2RnZSIpKw0KICBsYWJzKHRpdGxlID0gIllPVVRIIERJU0NPTk5FQ1RJT04gQlkgTEFUSU5YIFNVQkdST1VQIiwNCiAgICBzdWJ0aXRsZSA9ICJPUkRFUkVEIEJZIEFWRVJBR0UgRElTQ09OTkVDVElPTiBMRVZFTCBPRiAyMDE3ICYgMjAxOCIsDQogICAgICAgICAgIHkgPSAiWU9VVEggRElTQ09OTkVDVElPTiAoJSkiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWwpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZSgNCiAgbGltaXRzID0gYyhsZXZlbHMocHVsbChsYXRpbnhfZm9yX3Bsb3QsUmFjZV9FdGhuaWNpdHkpKVsxXSwNCiAgICAgICAgICAgICAiQmxhbmsiLA0KICAgICAgICAgICAgICJORVhUX2JsYW5rIiwNCiAgICAgICAgICAgICBsZXZlbHMocHVsbChsYXRpbnhfZm9yX3Bsb3QsUmFjZV9FdGhuaWNpdHkpKVsyOg0KICAgICAgICAgICAgIGxlbmd0aChsZXZlbHMocHVsbChsYXRpbnhfZm9yX3Bsb3QsUmFjZV9FdGhuaWNpdHkpKSldKSwNCiAgbGFiZWxzID0gYygiTGF0aW54IiA9ICJMYXRpbnggQXZnLiIsDQogICAgICAgICAgICAgIkJsYW5rIiA9ICIiLA0KICAgICAgICAgICAgICJORVhUX2JsYW5rIj0gIiIpKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCxtYXgocHVsbChsYXRpbnhfc3ViZ3JvdXBzLFBlcmNlbnQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpKSsNCiAgYmFyX3RoZW1lKCkrDQogICN0aGlzIGFkZCB0aGUgbGVnZW5kIHRvIHRoZSBwbG90IGFyZWEhOg0KICB0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMC4yMywwLjAxKSxsZWdlbmQucG9zaXRpb24gPSBjKDAuMjMsLjAxKSkrDQogIGdlb21fdGV4dChkYXRhID0gZGZfbGF0aW54X3RvdGFsLA0KICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gMywgeSA9IFBlcmNlbnQgKy43LCBsYWJlbCA9IGxhYmVsKSwNCiAgICAgICAgICAgcGFyc2UgPSBUUlVFLA0KICAgICAgICAgICBjb2xvciA9ICIjMDA4MzkzIiwgDQogICAgICAgICAgICBzaXplID0gNCkgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gcHVsbChkZl9sYXRpbnhfdG90YWwsIFBlcmNlbnQpKSwgbGluZXR5cGUgPSAyKSArDQogIGdlb21fdGV4dChkYXRhID0gZGZfVVNfdG90YWwsDQogICAgICAgICBtYXBwaW5nID0gYWVzKHggPSAzLCB5ID0gUGVyY2VudCArLjcsIGxhYmVsID0gbGFiZWwpLA0KICAgICAgICAgICBwYXJzZSA9IFRSVUUsDQogICAgICAgICAgIGNvbG9yID0gIiMwMDgzOTMiLCANCiAgICAgICAgICAgIHNpemUgPSA0KSArDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBwdWxsKGRmX1VTX3RvdGFsLCBQZXJjZW50KSksIGxpbmV0eXBlID0gMykNCmBgYA0KDQo8L2RldGFpbHM+IA0KDQpgYGB7cn0NCg0KbGF0aW54X3N1Ymdyb3VwX3Bsb3Rfc2ltcA0KYGBgDQoNCg0KIyMgKipEYXRhIEFuYWx5c2lzKioNCioqKiANCg0KSWYgeW91IGhhdmUgYmVlbiBmb2xsb3dpbmcgYWxvbmcgYnV0IHN0b3BwZWQgYW5kIGFyZSBzdGFydGluZyBoZXJlIHlvdSBjb3VsZCBsb2FkIHRoZSB3cmFuZ2xlZCBkYXRhIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoNCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgd3JhbmdsaW5nIHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5Pg0KDQpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSBgT0NTZGF0YWAgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmBgYA0KDQpUaGVuLCB5b3UgbWF5IGxvYWQgdGhlIHdyYW5nbGVkIGRhdGEgaW50byB5b3VyIFIgZW52aXJvbm1lbnQgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgbGlicmFyeShPQ1NkYXRhKQ0Kd3JhbmdsZWRfcmRhKCJvY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbiIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQpJZiB0aGUgcGFja2FnZSBkb2VzIG5vdCB3b3JrIGZvciB5b3UsIGFuIFJEQSBmaWxlIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAteW91dGgtZGlzY29ubmVjdGlvbi90cmVlL21hc3Rlci9kYXRhL3dyYW5nbGVkKSBvciBzbGlnaHRseSBtb3JlIGRpcmVjdGx5IFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC15b3V0aC1kaXNjb25uZWN0aW9uL21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiANCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQoNCjwvZGV0YWlscz4NCioqKg0KDQpJbiBvdXIgZGF0YSwgd2UgaGF2ZSBwb29sZWQgKHJlcGVhdGVkKSBbY3Jvc3Mtc2VjdGlvbmFsXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Dcm9zcy1zZWN0aW9uYWxfZGF0YT9vbGRmb3JtYXQ9dHJ1ZSkgZGF0YS4NCg0KVGhpcyBpcyBkYXRhIHByb2R1Y2VkIGZyb20gcmVwZWF0ZWQgbWVhc3VyZW1lbnQgb2YgYSBbcG9wdWxhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUG9wdWxhdGlvbj9vbGRmb3JtYXQ9dHJ1ZSkgb3ZlciB0aW1lLg0KDQpJdCBpcyBvZnRlbiB1bmZlYXNpYmxlIHRvIGNvbGxlY3QgZGF0YSBmb3IgYW4gZW50aXJlIHBvcHVsYXRpb24gYXQgb25jZS4gSG93ZXZlciwgd2UgY2FuIHN0aWxsIG9idGFpbiBtZWFuaW5nZnVsIG1lYXN1cmVzIHVzaW5nIGEgcmFuZG9tIFtzYW1wbGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NhbXBsaW5nXyhzdGF0aXN0aWNzKT9vbGRmb3JtYXQ9dHJ1ZSkgb2YgdGhlIHBvcHVsYXRpb24uIA0KDQpBdCBzcGVjaWZpYyB0aW1lLXBvaW50cywgZGF0YSBpcyBjb2xsZWN0ZWQgZnJvbSBhIHNhbXBsZSBvZiB0aGUgcG9wdWxhdGlvbi4gVGhlIGluZGl2aWR1YWxzIGluIGVhY2ggc2FtcGxlIGFyZSBub3QgbmVjZXNzYXJpbHkgdGhlIHNhbWUgaW5kaXZpZHVhbHMuIFRoaXMgc2VwYXJhdGVzIHBvb2xlZCBjcm9zcy1zZWN0aW9uYWwgZGF0YSBmcm9tIHBhbmVsIGRhdGEsIHdoaWNoIGlzIGxvbmdpdHVkaW5hbCBkYXRhIGZyb20gcmVwZWF0ZWQgbWVhc3VyZW1lbnQgb2YgdGhlIHNhbWUgcGVvcGxlLiAgIA0KDQpCeSBzYW1wbGluZyBmcm9tIGEgcG9wdWxhdGlvbiBhdCBtdWx0aXBsZSB0aW1lIHBvaW50cywgd2UgY2FuIGdlbmVyYXRlIGVzdGltYXRlcyBvZiBwb3B1bGF0aW9uIGxldmVsIHN0YXRpc3RpY3MuIEFsdGhvdWdoIHRoZXNlIHN0YXRpc3RpY3MgaGF2ZSBzb21lIHJhbmRvbSBlcnJvciwgdGhleSBjYW4gcHJvdmlkZSBpbnNpZ2h0IGludG8gaG93IHRoZSBtZWFzdXJlIHZhcmlhYmxlIGlzIGNoYW5naW5nIGluIGEgcG9wdWxhdGlvbiBvdmVyIHRpbWUuDQoNCg0KQXMgd2UgaGF2ZSBtZWFzdXJlZCB2YWx1ZXMgb3ZlciB0aW1lLCB3ZSBjYW4gcGxvdCB0aGUgZGF0YSB0byBzZWUgaWYgdGhlcmUgaXMgYSB0cmVuZCAoaW5jcmVhc2luZywgZGVjcmVhc2luZywgbW9ub3RvbmljLCBub24tbW9ub3RvbmljLCBsaW5lYXIsIG5vbi1saW5lYXIsIGV0YykuIFNvbWV0aW1lcywgaG93ZXZlciwgdGhlIHRyZW5kIGlzbid0IGV4YWN0bHkgY2xlYXIuIEZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgc3RhdGlzdGljYWwgbWV0aG9kcyB0byByZXNvbHZlIHRoaXMgaXNzdWUuDQoNCk5leHQsIHdlIHdpbGwgaW50cm9kdWNlIGFuZCBkaXNjdXNzIHRoZSBNYW5uLUtlbmRhbGwgdHJlbmQgdGVzdC4gDQoNCiMjIyAqKk1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0KioNCioqKg0KVGhlIE1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0LCAod2hpY2ggaXMgYSB2YXJpYXRpb24gb2YgdGhlIFtLZW5kYWxsIHJhbmsgY29ycmVsYXRpb24gY29lZmZpY2llbnRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0tlbmRhbGxfcmFua19jb3JyZWxhdGlvbl9jb2VmZmljaWVudD9vbGRmb3JtYXQ9dHJ1ZSkpLCB0ZXN0cyB3aGV0aGVyIHRoZXJlIGlzIGEgW21vbm90b25pYyBhc3NvY2lhdGlvbl0oaHR0cHM6Ly93d3cuc3RhdGlzdGljc2hvd3RvLmNvbS9tb25vdG9uaWMtcmVsYXRpb25zaGlwLykgYmV0d2VlbiB0d28gdmFyaWFibGVzLCBvciBhIHJlbGF0aW9uc2hpcCB3aXRoIGNvbnNpc3RlbnQgZGlyZWN0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcywgd2hlcmUgb25lIHZhcmlhYmxlICh0eXBpY2FsbHkgdGltZSkgZGV0ZXJtaW5lcyB0aGUgb3JkZXIgb2YgdGhlIG90aGVyIHZhcmlhYmxlLiBUaHVzIHRoaXMgdGVzdCBpcyBtb3N0IG9mdGVuIHVzZWQgdG8gc2VlIGlmIHRoZXJlIGlzIGEgY29uc2lzdGVudCBjaGFuZ2UgaW4gZGlyZWN0aW9uIChvciBbbWF0aGVtYXRpY2FsIHNpZ25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NpZ25fKG1hdGhlbWF0aWNzKSkpIGZvciBhIHZhcmlhYmxlIGFjcm9zcyB0aW1lLg0KDQpTZWUgdGhpcyBbbGlua10oaHR0cHM6Ly92c3AucG5ubC5nb3YvaGVscC92c2FtcGxlL0Rlc2lnbl9UcmVuZF9NYW5uX0tlbmRhbGwuaHRtKSBmb3IgaW5mb3JtYXRpb24gb24gdXNpbmcgdGhpcyB0ZXN0IHdpdGggbWlzc2luZyBkYXRhLg0KDQojIyMgKipNb25vdG9uaWMgcmVsYXRpb25zaGlwcyoqDQoqKioNCkFzIHlvdSBjYW4gc2VlIGluIHRoZSBpbWFnZSBiZWxvdywgYSBtb25vdG9uaWMgcmVsYXRpb25zaGlwIGRvZXMgbm90IG5lZWQgdG8gYmUgbGluZWFyLCBpdCBzaW1wbHkgbmVlZHMgdG8gc2hvdyBhIGNvbnNpc3RlbnQgZGlyZWN0aW9uIG9mIGNoYW5nZS4gQSBsaW5lYXIgcmVsYXRpb25zaGlwIG9uIHRoZSBvdGhlciBoYW5kIHNob3dzIGNvbnNpc3RlbnQgZGlyZWN0aW9uIGFuZCByYXRlIG9mIGNoYW5nZS4NCg0KYGBge3IsIG91dC53aWR0aD0iNDAwcHgiLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTcvMDMvbW9ub3RvbmljLXJlbGF0aW9uc2hpcC5wbmciKQ0KYGBgDQoNCg0KIyMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5jb20vbW9ub3RvbmljLXJlbGF0aW9uc2hpcC8pIC0gU3RlcGhhbmllIEdsZW4uICJNb25vdG9uaWMgUmVsYXRpb25zaGlwOiBEZWZpbml0aW9uIiBGcm9tIFN0YXRpc3RpY3NIb3dUby5jb206IEVsZW1lbnRhcnkgU3RhdGlzdGljcyBmb3IgdGhlIHJlc3Qgb2YgdXMhIGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5jb20vbW9ub3RvbmljLXJlbGF0aW9uc2hpcC9dDQoNCg0KU28gaW4gb3RoZXIgd29yZHMsIHdlIGFyZSBpbnRlcmVzdGVkIHRvIHNlZSBpZiB0aGVyZSBpcyBhIGNvbnNpc3RlbnQgZGlyZWN0aW9uIGluIHRoZSByZWxhdGlvbnNoaXAgb2YgZGlzY29ubmVjdGlvbiByYXRlcyB3aXRoIHRpbWUsIGFzIHdlIHdhbnQgdG8ga25vdyBpZiByYXRlcyBoYXZlIGJlZW4gY29uc2lzdGVudGx5IGluY3JlYXNpbmcgb3IgZGVjcmVhc2luZyBvciBpZiB0aGV5IGhhdmUgYmVlbiBzdGF5aW5nIHJvdWdobHkgdGhlIHNhbWUuIA0KDQoqKk5vdGUqKjogdGhpcyB0ZXN0IGRvZXMgbm90IHRlc3QgaWYgdGhlIHJhdGUgb2YgY2hhbmdlIGhhcyBiZWVuIGNvbnNpc3RlbnQgb3ZlciB0aW1lLCBqdXN0IHRoZSBkaXJlY3Rpb24uDQoNCiMjIyMgTWFubi1LZW5kYWxsIHRyZW5kIHRlc3QgdnMgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uDQoqKioNCg0KSW4gY29udHJhc3QgdG8gdGhlIE1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0LCB3ZSBicmllZmx5IGNvbXBhcmUgdGhpcyB0byBbU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TaW1wbGVfbGluZWFyX3JlZ3Jlc3Npb24pLiANCg0KVGhlcmUgYXJlIGEgY291cGxlIG9mIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvOiANCg0KMS4gTGluZWFyIHJlZ3Jlc3Npb24gYXNzZXNzZXMgaWYgdGhlcmUgYSBjb25zaXN0ZW50IGNoYW5nZSBpbiBib3RoIHJhdGUgYW5kIGRpcmVjdGlvbiBpbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFzIGEgc2luZ2xlIGluZGVwZW5kZW50IHZhcmlhYmxlIGNoYW5nZXMuIFRoZSBjb2VmZmljaWVudHMgdGVsbCB1cyBob3cgbXVjaCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGNoYW5nZXMgd2l0aCBvbmUgdW5pdCBjaGFuZ2Ugb2YgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlLiANCg0KMi4gVGhlIE1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0IGlzIGEgW25vbnBhcmFtZXRyaWNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL05vbnBhcmFtZXRyaWNfc3RhdGlzdGljcykgdGVzdCwgd2hpY2ggbWVhbnMgdGhhdCBpdCBkb2VzIG5vdCByZXF1aXJlIGFzIG1hbnkgYXNzdW1wdGlvbnMgYXMgc29tZSBbcGFyYW1ldHJpY10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUGFyYW1ldHJpY19zdGF0aXN0aWNzKSB0ZXN0cywgbGlrZSB0aGUgW3NpbXBsZSBsaW5lYXIgcmVncmVzc2lvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX2xpbmVhcl9yZWdyZXNzaW9uKS4NCg0KDQojIyMgKipNLUsgdGVzdCBieSBoYW5kKioNCioqKg0KVGhlIFtudWxsIGh5cG90aGVzaXNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL051bGxfaHlwb3RoZXNpcykgJEhfezB9JCBvZiB0aGUgdGVzdCBpcyB0aGF0IHRoZXJlIGlzIG5vIG1vbm90b25pYyB0cmVuZCwgd2hpbGUgdGhlIFthbHRlcm5hdGl2ZSBoeXBvdGhlc2lzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BbHRlcm5hdGl2ZV9oeXBvdGhlc2lzKSAkSF97YX0kIGlzIHRoYXQgdGhlcmUgaXMgYSBtb25vdG9uaWMgdHJlbmQuDQoNCkFjY29yZGluZyB0byBbdGhpcyByZXBvcnRdKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDgvTWFubi1LZW5kYWxsLUFuYWx5c2lzLTEucGRmKSwgdGhlIE1hbm4tS2VuZGFsbCAkUyQgc2NvcmUgaXMgY2FsY3VsYXRlZCBhcyBmb2xsb3dzOg0KDQo+IFRoZSBkYXRhIHZhbHVlcyBhcmUgZXZhbHVhdGVkIGFzIGFuIG9yZGVyZWQgdGltZSBzZXJpZXMuIEVhY2ggZGF0YSB2YWx1ZSBpcyBjb21wYXJlZCB0byBhbGwgc3Vic2VxdWVudCBkYXRhIHZhbHVlcy4gVGhlIGluaXRpYWwgdmFsdWUgb2YgdGhlIE1hbm4tS2VuZGFsbCBzdGF0aXN0aWMsICRTJCwgaXMgYXNzdW1lZCB0byBiZSAwIChlLmcuLCBubyB0cmVuZCkuIElmIGEgZGF0YSB2YWx1ZSBmcm9tIGEgbGF0ZXIgdGltZSBwZXJpb2QgaXMgaGlnaGVyIHRoYW4gYSBkYXRhIHZhbHVlIGZyb20gYW4gZWFybGllciB0aW1lIHBlcmlvZCwgJFMkIGlzIGluY3JlbWVudGVkIGJ5IDEuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiB0aGUgZGF0YSB2YWx1ZSBmcm9tIGEgbGF0ZXIgdGltZSBwZXJpb2QgaXMgbG93ZXIgdGhhbiBhIGRhdGEgdmFsdWUgc2FtcGxlZCBlYXJsaWVyLCAkUyQgaXMgZGVjcmVtZW50ZWQgYnkgMS4gVGhlIG5ldCByZXN1bHQgb2YgYWxsIHN1Y2ggaW5jcmVtZW50cyBhbmQgZGVjcmVtZW50cyB5aWVsZHMgdGhlIGZpbmFsIHZhbHVlIG9mICRTJC4gDQoNCg0KJCRTID1cc3VtX3trPTF9XntuLTF9XHN1bV97aiA9IGsrMX1ee259IHNpZ24oeF97an0gLSB4X3trfSkgJCQNCg0Kd2hlcmUgd2UgYXNzdW1lIHRoZXJlIGFyZSAkbiQgdGltZSBwb2ludHMgYW5kIG9ic2VydmF0aW9ucyAkeF97an0kIGFuZCAkeF97a30kIGF0IHR3byB0aW1lIHBvaW50cyBhbmQ6DQoNCiQkc2lnbih4X3tqfS14X3trfSkgPSAxIFx0ZXh0eywgaWYgfSB4X3tqfS14X3trfT4wJCQNCiAkJHNpZ24oeF97an0teF97a30pID0gMCBcdGV4dHssIGlmIH0geF97an0teF97a309MCQkDQogJCRzaWduKHhfe2p9LXhfe2t9KSA9IC0xIFx0ZXh0eywgaWYgfSB4X3tqfS14X3trfTwwJCQNCiAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICANCkFub3RoZXIgd2F5IG9mIHN0YXRpbmcgdGhpcyBpcyBhbGwgcG9zc2libGUgY2FzZXMgd2hlcmUgYSB2YWx1ZSBvY2N1cnJlZCBsYXRlciBpbiB0aW1lIG11c3QgYmUgY29tcGFyZWQgd2l0aCB2YWx1ZXMgdGhhdCBvY2N1cnJlZCBlYXJsaWVyIGluIHRpbWUsIGdpdmluZyAkbihu4oCTMSkvMiQgY29tcGFyaXNvbnMsIGxpa2Ugc28gaWYgdGhlcmUgd2VyZSBmb3VyIHRpbWUgcG9pbnRzICgkbiA9IDQkKToNCg0KJCRTID0gc2lnbih4XzLiiJJ4XzEpICsgc2lnbih4XzPiiJJ4XzEpICsgc2lnbih4XzQteF8xKSArIHNpZ24oeF8z4oiSeF8yKSArIHNpZ24oeF80LXhfMikgKyBzaWduKHhfNC14XzMpJCQNCg0Kd2hpY2ggaXMgY29uc2lzdGVudCB3aXRoICQ0KDQtMSkvMiA9IDQoMykvMiA9IDEyLzIgPSA2JCBjb21wYXJpc29ucy4NCg0KT25jZSB3ZSBoYXZlIGNhbGN1bGF0ZWQgdGhlIE1hbm4tS2VuZGFsbCAkUyQgc2NvcmUsIHdlIHdhbnQgdG8ga25vdyBpZiB0aGUgc2NvcmUgaXMgbWVhbmluZ2Z1bGx5IGxhcmdlIG9yIHNtYWxsIGVub3VnaCBmcm9tIHdoYXQgd2Ugd291bGQgZXhwZWN0IHdpdGggZGF0YSB3aXRoIG5vIHRyZW5kLiBUbyBkZXRlcm1pbmUgdGhhdCwgd2UgbmVlZCB0byBrbm93IHdoYXQgdGhlIF92YXJpYW5jZV8gaXMgb2YgdGhlIE1hbm4tS2VuZGFsbCAkUyQgc2NvcmUgKG9yIGhvdyBtdWNoIHZhcmlhYmlsaXR5IHdlIGV4cGVjdCkuIA0KDQpXZSB3b24ndCBleHBsYWluIHRoZSBkZXRhaWxzIGhlcmUsIGJ1dCBpdCB0dXJucyBvdXQgdGhlIHZhcmlhbmNlIG9mIHRoZSBNYW5uLUtlbmRhbGwgJFMkIHNjb3JlIChvciAkXHRleHR7dmFyfShTKSQpIGlzIGNhbGN1bGF0ZWQgbGlrZSBzbzoNCg0KJCQgXHRleHR7dmFyfShTKT1cZnJhY3sxfXsxOH0obihu4oiSMSkoMm4rNSniiJJcc3VtX3twLTF9XntnfXRfe3B9KHRfe3B94oiSMSkoMnRfe3B9KzUpKSQkDQp3aGVyZTogDQoNCiQkZyA9ICBcdGV4dHtudW1iZXIgb2YgdGllZCBncm91cHN9JCQNCiQkdF97cH0gPSAgXHRleHR7bnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgfXBee3RofVx0ZXh0eyBncm91cCBmb3IgYSBnaXZlbiB0aWV9JCQNCg0KRm9yIGV4YW1wbGUsIGlmIDMgb2JzZXJ2YXRpb25zIGluIG91ciBleGFtcGxlIHdlcmUgMzUsIHRoZW4gdGhlcmUgd291bGQgYmUgMSAkZyQgdGllZCBncm91cCBhbmQgMyAkdF97cH0kIG9ic2VydmF0aW9ucy4gDQoNCioqTm90ZSoqOiBhY2NvcmRpbmcgdG8gdGhpcyBbc291cmNlXShodHRwczovL3d3dy5zdGF0aXN0aWNzaG93dG8uY29tL21hbm4ta2VuZGFsbC10cmVuZC10ZXN0LyksIGEgbWluaW11bSBvZiA0IG9ic2VydmF0aW9ucyBzaG91bGQgYmUgdXNlZCBmb3IgdGhpcyB0ZXN0IGFuZCBpdCBpcyByZWNvbW1lbmRlZCB0byBoYXZlIDggb3IgbW9yZS4gDQoNClB1dHRpbmcgdGhpcyBhbGwgdG9nZXRoZXIsIHRoZSB0ZXN0IHN0YXRpc3RpYyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiAodG8ga25vdyBpZiB0aGUgc2NvcmUgaXMgbWVhbmluZ2Z1bGx5IGxhcmdlIG9yIHNtYWxsIGVub3VnaCBmcm9tIHdoYXQgd2Ugd291bGQgZXhwZWN0IHdpdGggZGF0YSB3aXRoIG5vIHRyZW5kKSAkWl97TUt9JCBpcyBjYWxjdWxhdGVkIGFzIGZvbGxvd3M6DQoNCiBpZiAkUyA+IDAkIHRoZW4gJFpfe01LfSA9IFxmcmFje1MtMX17XHNxcnR7XHRleHR7dmFyfShTKX19JCAgDQogaWYgJFMgPCAwJCB0aGVuICRaX3tNS30gPSBcZnJhY3tTKzF9e1xzcXJ0e1x0ZXh0e3Zhcn0oUyl9fSQgIA0KIGlmICRTID0gMCQgdGhlbiAkWl97TUt9ID0gMCQgIA0KDQpUaGlzIHRlc3Qgc3RhdGlzdGljICRaX3tNS30kIGlzIGNvbnNpZGVyZWQgYSBbWiBzY29yZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3RhbmRhcmRfc2NvcmUpIChhbHNvIGtub3duIGFzIGEgc3RhbmRhcmQgb3Igc3RhbmRhcmRpemVkIHNjb3JlKS4gV2UgY2FuIHVzZSBzdGFuZGFyZCBsb29rIHVwIHRhYmxlcyBmcm9tIHRoZXJlIHRvIGNhbGN1bGF0ZSBhIHAtdmFsdWUuIFdlIHdpbGwgd2FsayB0aHJvdWdoIGFuIGV4YW1wbGUgb2YgaG93IHRvIGRvIHRoaXMgYmVsb3cuIA0KDQpMZXQncyBjb25kdWN0IGEgTWFubi1LZW5kYWxsIHRyZW5kIHRlc3QgdXNpbmcgb3VyIGRhdGEgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkgbW9ub3RvbmljIHRyZW5kcyBpbiB5b3V0aCBkaXNjb25uZWN0aW9uIGFjcm9zcyB0aW1lLiANCg0KIyMjIyBNLUsgdHJlbmQgdGVzdCBmb3IgTmF0aXZlIEFtZXJpY2Fucw0KKioqDQoNClJlY2FsbCB0aGF0IHRoZSB5b3V0aCBkaXNjb25uZWN0aW9uIHJhdGVzIGZvciBOYXRpdmUgQW1lcmljYW5zIHdlcmUgc29tZSBvZiB0aGUgaGlnaGVzdCBpbiB0aGUgZmlyc3QgdGFibGUgd2UgZXhhbWluZWQuIA0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiODAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImRhdGEiLCAicmF3IiwgIk1ham9yX2V0aG5pY19ncm91cHNfc2NyZWVuc2hvdC5wbmciKSkNCmBgYA0KDQphbmQgdGhlIHBsb3Qgd2UgbWFkZTogDQoNCmBgYHtyfQ0KeW91dGhfcGxvdA0KYGBgDQoNCkxldCdzIGNvbnNpZGVyIGp1c3Qgb2JzZXJ2ZWQgdmFsdWVzIGZvciB0aGUgTmF0aXZlIEFtZXJpY2FuIHN1Ymdyb3VwLiBUaGUgZmlyc3QgdmFsdWUgaW4gdGltZSB3YXMgKCR4XzEkID0gdGltZSBwb2ludCAxKSAyNC40LCB0aGVuICgkeF8yJCkgMjguOCBhdCB0aGUgbmV4dCB0aW1lIHBvaW50LCB0aGVuICgkeF8zJCkgMjcuMCwgdGhlbiAoJHhfNCQpIDI2LjMsIHRoZW4gKCR4XzUkKSAyNS44LCBhbmQgZmluYWxseSAoJHhfNiQpIDIzLjkuDQoNCklmIHdlIHdlcmUgdG8gY2FsY3VsYXRlIHRoZSBNYW5uLUtlbmRhbGwgc3RhdGlzdGljIG1hbnVhbGx5IHdlIGNvdWxkIGRvIHRoZSBmb2xsb3dpbmc6DQoNCiQkICBTID0gc2lnbih4XzLiiJJ4XzEpICsgc2lnbih4XzMteF8xKSArIHNpZ24oeF80LXhfMSkgKyBzaWduKHhfNS14XzEpICsgc2lnbih4XzYteF8xKSArJCQNCg0KJCQgc2lnbih4XzMteF8yKSArIHNpZ24oeF80LXhfMikgKyBzaWduKHhfNS14XzIpICsgc2lnbih4XzYteF8yKSArJCQgIA0KDQokJHNpZ24oeF80LXhfMykgKyBzaWduKHhfNS14XzMpICsgc2lnbih4XzYteF8zKSArJCQgIA0KDQokJHNpZ24oeF81LXhfNCkgKyBzaWduKHhfNi14XzQpICskJCAgIA0KDQokJHNpZ24oeF82LXhfNSkkJCAgDQoNClRodXMgdG8gY2FsY3VsYXRlICRTJCBtYW51YWxseSBpbiBSIHdlIGNvdWxkIGRvIGxpa2Ugc286DQoNCmBgYHtyfQ0KeDEgPSAyNC40DQp4MiA9IDI4LjgNCngzID0gMjcNCng0ID0gMjYuMw0KeDUgPSAyNS44DQp4NiA9IDIzLjkNCg0Kc2lnbih4Mi14MSkgKyBzaWduKHgzLXgxKSArIHNpZ24oeDQteDEpICsgc2lnbih4NS14MSkgKyBzaWduKHg2LXgxKSArDQpzaWduKHgzLXgyKSArIHNpZ24oeDQteDIpICsgc2lnbih4NS14MikgKyBzaWduKHg2LXgyKSArc2lnbih4NC14MykgKyBzaWduKHg1LXgzKSArIHNpZ24oeDYteDMpICtzaWduKHg1LXg0KSArIHNpZ24oeDYteDQpICsgc2lnbih4Ni14NSkNCmBgYA0KDQpUaGUgcmVzdWx0IGlzIC03LiANCg0KVGhlICRcdGV4dHt2YXJ9KFMpJCB3b3VsZCBiZSBjYWxjdWxhdGVkIGxpa2Ugc28gKHNpbmNlIHdlIGhhdmUgJG4kID0gNiBvYnNlcnZhdGlvbnMgYW5kIG5vIHRpZXMpOg0KDQokJCBcdGV4dHt2YXJ9KFMpPVxmcmFjezF9ezE4fSg2KDbiiJIxKSgyKDYpKzUp4oiSXHN1bV97cC0xfV57Z30wKDDiiJIxKSgyKDApKzUpKSQkDQokJCBcdGV4dHt2YXJ9KFMpPVxmcmFjezF9ezE4fSgzMCkoMTcp4oiSMCA9MS42NjY2NjcoMTcpID0gMjguMzMzMzMkJA0KDQpGaW5hbGx5LCB3ZSBjYWxjdWxhdGUgdGhlIHN0YW5kYXJkaXplZCBzY29yZSAkWl97TUt9JCAod2l0aCAkUyQgPCAwKToNCg0KICRaX3tNS30gPSBcZnJhY3tTKzF9e1xzcXJ0e1ZBUihTKX19JA0KICRaX3tNS30gPSBcZnJhY3stNysxfXtcc3FydHsyOC4zMzMzM319ID0gXGZyYWN7LTZ9ezUuMzIyOTA2fSA9IC0xLjEyNyQNCiANCkFzIHdlIG5vdGVkIGFib3ZlLCB0aGUgJFpfe01LfSQgaXMgdGhlIHNwZWNpZmljIE1hbm4tS2VuZGFsbCB2ZXJzaW9uIG9mIHRoZSBbWiBzY29yZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3RhbmRhcmRfc2NvcmUpLiBUaGlzIGFsbG93cyB1cyB0byB1c2UgYSB2YXJpZXR5IG9mIGRhdGEgKHdoaWNoIG1heWJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIG9yIG5vdCkgYW5kIGNyZWF0ZSBhIHN0YW5kYXJkIHN0YXRpc3RpYyBhYm91dCB0aGUgZGF0YSB0aGF0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiANCg0KVXNpbmcgdGhlICRaX3tNS30kIFogc2NvcmUsIHdlIGNhbiB1c2UgYSBzdGFuZGFyZCAkWiQgdGFibGUgdG8gZGV0ZXJtaW5lIHRoZSAkcCQtdmFsdWUuIA0KDQpUaGUgJHAkLXZhbHVlIHRlbGxzIHVzIHRoZSBwcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgYSB0ZXN0IHN0YXRpc3RpYyBhcyBvciBtb3JlIGV4dHJlbWUgdGhhbiB0aGUgb25lIHdlIG9ic2VydmVkIGlmIHRoZSBudWxsIGh5cG90aGVzaXMgKHRoYXQgdGhlcmUgaXMgbm8gdHJlbmQpIGlzIHRydWUuIElmIHRoZSAkcCQtdmFsdWUgaXMgc21hbGwsIHNheSBsZXNzIHRoYW4gMC4wNSwgdGhlbiBvdXIgb2JzZXJ2ZWQgJFpfe01LfSQgc3RhdGlzdGljIGlzIHByZXR0eSByYXJlIGlmIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gVGhpcyB3b3VsZCBsZWFkIHVzIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGFuZCBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIGluZGVlZCBhIHRyZW5kIGluIHRoZSBkYXRhLiBXZSB3aWxsIHRhbGsgbW9yZSBhYm91dCAkcCQtdmFsdWVzLCBhbmQgaW4gcGFydGljdWxhciBvdXIgY2hvaWNlIG9mIDAuMDUgYXMgYSBjb21wYXJpc29uIGZvciB0aGUgJHAkLXZhbHVlLCBhIGxpdHRsZSBsYXRlci4gU2VlIHRoaXMgW2Nhc2Ugc3R1ZHldKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkvKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBwcm9iYWJpbGl0eSBhbmQgaHlwb3RoZXNpcyB0ZXN0aW5nLg0KDQpPSyBiYWNrIHRvIG91ciBleGFtcGxlLiBMZXQncyBmaWd1cmUgb3V0IHdoYXQgb3VyICRwJC12YWx1ZSBpcyB1c2luZyB0aGUgc3RhbmRhcmQgJFokIHNjb3JlIHRhYmxlLg0KDQpVc2luZyB0aGUgdGFibGUgYmVsb3csIGluIHRoZSB0aGUgJFokIGNvbHVtbiBvbiB0aGUgZmFyIGxlZnQsIHdlIGZpbmQgYSByb3cgdGhhdCBzYXlzIC0xLjEgYW5kIGJlY2F1c2Ugb3VyICRaJCB2YWx1ZSBpcyAtMS4xMjcgd2Ugcm91bmQgdGhpcyB0byAtMS4xMyBhbmQgdGhlbiBmaW5kIHRoZSBjb2x1bW4gZm9yIDAuMDMgdG8gYWNjb3VudCBmb3IgdGhlIHZhbHVlIHRoYXQgaXMgc2Vjb25kIGFmdGVyIGRlY2ltYWwgcGxhY2UgZm9yIG91ciAkWiQgc2NvcmUuIFdlIHNlZSB0aGF0IHRoZSB2YWx1ZSB3aGVyZSB0aGlzIHJvdyBhbmQgdGhpcyBjb2x1bW4gaW50ZXJzZWN0IGlzIDAuMTI5Mi4gVGhpcyBpcyB0aGUgJHAkLXZhbHVlLCB0aGF0IGluZGljYXRlcyB0aGUgcHJvYmFiaWxpdHkgb2YgaGF2aW5nIGEgJFokIHNjb3JlIHRoYXQgaXMgbW9yZSBleHRyZW1lIChvbiB0aGUgbmVnYXRpdmUgc2lkZSkgb2YgdGhlIFskWiQgc2NvcmUgbm9ybWFsIGRpc3RyaWJ1dGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3RhbmRhcmRfc2NvcmUpIG9yIHRoZSBjb2xvcmVkIHBvcnRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBpbiB0aGUgZmlndXJlIGJlbG93LiBJbiBvdGhlciB3b3JkcywgdGhlcmUgaXMgYSAxMyUgY2hhbmNlIG9mIG9ic2VydmluZyBkYXRhIHdpdGggYSBtb3JlIG5lZ2F0aXZlICRaJCBzY29yZSAoYW5kIGFsc28gYSBkb3dud2FyZCB0cmVuZCBhcyB0aGUgc2lnbiBvZiB0aGUgJFpfe01LfSQgaXMgZGV0ZXJtaW5lZCBieSB0aGUgJFMkIHNjb3JlKSBzaW1wbHkgYnkgY2hhbmNlIGFsb25lLg0KIA0KYGBge3IsZWNobz1GQUxTRSwgb3V0LndpZHRoPSI4MCUifQ0KIGluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInpzY29yZS5wbmciKSkNCmBgYA0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vc29jcmF0aWMub3JnL3F1ZXN0aW9ucy81OTg2YTNlMWI3MmNmZjZmZDQ4YTU0MDgpDQoNClNpbmNlIHdlIGRvIG5vdCBrbm93IHdoYXQgdHlwZSBvZiB0cmVuZCB3ZSBhcmUgbG9va2luZyBmb3IgKHRodXMgd2UgYXJlIHVzaW5nIHdoYXQgaXMgY2FsbGVkIGEgW3R3by1zaWRlZCBoeXBvdGhlc2lzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PbmUtX2FuZF90d28tdGFpbGVkX3Rlc3RzKSAtIHdpdGggdGhlIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIG9yICRIX3thfSQgYmVpbmcgdGhhdCBlaXRoZXIgYSBwb3NpdGl2ZSBvciBuZWdhdGl2ZSB0cmVuZCBleGlzdHMpLCB3ZSBjYW4gdXNlIHRoaXMgdGFibGUgdG8gZGV0ZXJtaW5lIHRoYXQgdGhlICRwJC12YWx1ZSBpcyB+LjEyOTIgZm9yIGEgb25lLXNpZGVkIGh5cG90aGVzaXMgKHNwZWNpZmljYWxseSB0ZXN0aW5nIHRoYXQgYSBuZWdhdGl2ZSB0cmVuZCBleGlzdHMgb3IgdGVzdGluZyB0aGF0IGEgcG9zaXRpdmUgdHJlbmQgZXhpc3RzKSBhbmQgbXVsdGlwbHkgdGhpcyBieSAyIHRvIGdldCBhICRwJC12YWx1ZSBvZiBhcHByb3hpbWF0ZWx5IH4gMC4yNiBmb3IgYSB0d28tc2lkZWQgaHlwb3RoZXNpcy4NCg0KU2VlIHRoaXMgW2FydGljbGVdKGh0dHBzOi8vd3d3Lm9uZXNpZGVkLm9yZy9hcnRpY2xlcy90aGUtcGFyYWRveC1vZi1vbmUtc2lkZWQtdi10d28tc2lkZWQtdGVzdHMtb2Ytc2lnbmlmaWNhbmNlLnBocCkgZm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgb25lLXNpZGVkIGFuZCB0d28tc2lkZWQgdGVzdHMuDQoNClRodXMgaW4gb3RoZXIgd29yZHMsIHRoZXJlIGlzIGEgMjYlIGNoYW5jZSBvZiBvYnNlcnZpbmcgYSBtb3JlIHBvc2l0aXZlIG9yIG1vcmUgbmVnYXRpdmUgeiBzY29yZSBieSBjaGFuY2UgYWxvbmUuIFRoaXMgaXMgYSBwcmV0dHkgaGlnaCBjaGFuY2UhDQoNClRodXMgdmlzdWFsaXplIHRoaXMsIG91ciB0d28tc2lkZWQgdGVzdCBsb29rcyBmb3IgdGhlIGFyZWFzIG9uIHRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gb2YgdGhlIFogc2NvcmUgdGhhdCBhcmUgbW9yZSBwb3NpdGl2ZSBvciBtb3JlIG5lZ2F0aXZlIGZvciBib3RoIHNpZ25zIG9mIHRoZSAkWiQgdmFsdWU6DQoNCmBgYHtyLCBvdXQud2lkdGg9IjMwJSIsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAidHdvc2lkZWQucG5nIikpDQpgYGANCg0KQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSB0aGlzIFtjYWxjdWxhdG9yXShodHRwczovL3d3dy5zb2NzY2lzdGF0aXN0aWNzLmNvbS9wdmFsdWVzL25vcm1hbGRpc3RyaWJ1dGlvbi5hc3B4KSwgd2hpY2ggZ2l2ZXMgdXMgYSBtb3JlIHByZWNpc2UgJHAkLXZhbHVlIG9mIDAuMjYgZm9yIHRoZSB0d28tc2lkZWQgaHlwb3RoZXNpcy4NCiANCkVpdGhlciB3YXksIG91ciAkcCQtdmFsdWUgb2YgfiAwLjI2IHN1Z2dlc3RzIHRoYXQgd2UgZG8gbm90IGhhdmUgYSBtb25vdG9uaWMgdHJlbmQgYXMgb3VyICRwJC12YWx1ZSBpcyBtdWNoIGxhcmdlciB0aGFuIHRoZSB0eXBpY2FsIFtzaWduaWZpY2FuY2UgdGhyZXNob2xkXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdGF0aXN0aWNhbF9zaWduaWZpY2FuY2UpIG9mIDAuMDUuIFRoZSBpZGVhIGhlcmUgaXMgdGhhdCBpdCBpcyBhY2NlcHRhYmxlIHRoYXQgdGhlcmUgaXMgYSA1JSBbcHJvYmFiaWxpdHldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1Byb2JhYmlsaXR5KSB0aGF0IG91ciBvYnNlcnZhdGlvbiBvY2N1cnJlZCBzaW1wbHkgZHVlIHRvIGNoYW5jZS4gDQoNCkluIG91ciBjYXNlIHRoZXJlIGlzIG5vdCBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMuIFRodXMgd2UgY29uY2x1ZGUgdGhhdCB0aGVyZSBpcyBsaWtlbHkgbm8gdHJlbmQuDQoNCiMjIyAqKk0tSyB0ZXN0IGltcGxlbWVudGF0aW9uIGluIFIqKg0KKioqDQpXZSBjYW4gYWxzbyBhY2NvbXBsaXNoIHRoaXMgd2l0aCB0aGUgYE1hbm5LZW5kYWxsKClgIGZ1bmN0aW9uIG9mIHRoZSBgS2VuZGFsbGAgcGFja2FnZS4gVGhpcyBmdW5jdGlvbiByZXF1aXJlcyBhIHZlY3RvciBvZiBkYXRhIGZvciB3aGljaCBhIHRyZW5kIG1heSBiZSBvYnNlcnZlZC4gU2VlIHRoZSBbZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9yZHJyLmlvL2NyYW4vS2VuZGFsbC9tYW4vTWFubktlbmRhbGwuaHRtbCkgZm9yIHRoaXMgZnVuY3Rpb24gb2YgdGhlIGBLZW5kYWxsYCBwYWNrYWdlIGZvciBtb3JlIGRldGFpbHMuDQoNCiMjIyMgTS1LIHRyZW5kIHRlc3QgZm9yIE5hdGl2ZSBBbWVyaWNhbnMNCioqKg0KSW4gdGhlIGZvbGxvd2luZyB3ZTogDQoNCjEuIEZpbHRlciBmb3IganVzdCB0aGUgZGF0YSBmb3IgdGhlIE5hdGl2ZSBBbWVyaWNhbiBncm91cA0KMi4gVXNlIHRoZSBgcHVsbCgpYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlIHRvIGdldCBqdXN0IHRoZSB2ZWN0b3Igb2YgdGhpcyBkYXRhDQozLiBBcHBseSB0aGUgYE1hbm5LZW5kYWxsKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBLZW5kYWxsYCBwYWNrYWdlDQo0LiBVc2UgdGhlIGJhc2UgYHN1bW1hcnkoKWAgZnVuY3Rpb24gdG8gZ2V0IG1vcmUgaW5mb3JtYXRpb24gZm9yIHRoZSByZXN1bHRzIG9mIG91ciBNYW5uLUtlbmRhbGwgdHJlbmQgdGVzdA0KDQpgYGB7cn0NCm1ham9yX2dyb3Vwc19sb25nICU+JQ0KICBmaWx0ZXIoR2VuZGVyID09ICJBbGwiLCBSYWNlX0V0aG5pY2l0eSA9PSAiTmF0aXZlIEFtZXJpY2FuIikgJT4lDQogIHB1bGwoUGVyY2VudCkgJT4lDQogIE1hbm5LZW5kYWxsKC4pICU+JQ0KICBzdW1tYXJ5KCkNCmBgYA0KDQpBZ2FpbiwganVzdCBsaWtlIHdoZW4gd2UgY2FsY3VsYXRlZCB0aGlzIG1hbnVhbGx5LCB3ZSBzZWUgdGhhdCB0aGUgc2NvcmUgJFMkIGlzIC03LiBUaGUgbGFyZ2VyIHRoZSBzY29yZSwgdGhlIG1vcmUgaW5kaWNhdGlvbiB0aGF0IHRoZXJlIGlzIGFuIGluY3JlYXNpbmcgb3IgZGVjcmVhc2luZyBtb25vdG9uaWMgcmVsYXRpb25zaGlwLiBXZSBhbHNvIGFnYWluIHNlZSB0aGF0IHRoZSAkcCQtdmFsdWUgaXMgcm91Z2hseSA9IDAuMjYgYW5kIGlzIGdyZWF0ZXIgdGhhbiAwLjA1LCBzdWdnZXN0aW5nIHRoYXQgd2UgZmFpbGVkIHRvIGZpbmQgYSBtb25vdG9uaWMgdHJlbmQgaW4gdGhlIGRhdGEuIA0KDQpIb3dldmVyLCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgd2Ugb25seSBoYXZlIGByIGxlbmd0aChtYWpvcl9ncm91cHNfbG9uZyAlPiUgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgPT0gIk5hdGl2ZSBBbWVyaWNhbiIpICU+JSBwdWxsKFBlcmNlbnQpKWAgb2JzZXJ2YXRpb25zLiBUaHVzLCB3ZSBtYXkgbm90IGhhdmUgZW5vdWdoIG9ic2VydmF0aW9ucyB0byBvYnNlcnZlIGEgdHJlbmQuDQoNCldlIGNhbiBhbHNvIGV4cGxvcmUgd2hldGhlciB0aGVyZSBpcyBhICoqbGluZWFyKiogdHJlbmQgdXNpbmcgW3NpbXBsZSBsaW5lYXIgcmVncmVzc2lvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX2xpbmVhcl9yZWdyZXNzaW9uP29sZGZvcm1hdD10cnVlKSBieSB1c2luZyB0aGUgYGxtKClgIGZ1bmN0aW9uIG9mIHRoZSBgc3RhdHNgIHBhY2thZ2UuDQoNCkZpcnN0LCB3ZSB2aXN1YWxpemUgdGhlIGRhdGEgd2l0aCBgWWVhcmAgb24gdGhlIHgtYXhpcyBhbmQgYFBlcmNlbnRgIG9mIHlvdXRoIGRpc2Nvbm5lY3Rpb24gb24gdGhlIHktYXhpcy4NCg0KYGBge3J9DQptYWpvcl9ncm91cHNfbG9uZyAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwgUmFjZV9FdGhuaWNpdHkgPT0gIk5hdGl2ZSBBbWVyaWNhbiIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gUGVyY2VudCkpICsNCiAgICBnZW9tX3BvaW50KCkgDQpgYGANCg0KV2UgYXJlIGludGVyZXN0ZWQgaW4gbW9kZWxpbmcgaG93IHRoZSBwZXJjZW50IGRpc2Nvbm5lY3Rpb24gY2hhbmdlcyB3aXRoIHRpbWUsIHRodXMgd2Ugd2lsbCB1c2UgdGhlIGZvcm11bGEgYFBlcmNlbnQgfiBZZWFyYCBpbiB0aGUgYGxtKClgIGZ1bmN0aW9uLCB3aGljaCB1c2VzIHRoZSBgUGVyY2VudGAgYW5kIGBZZWFyYCB2YXJpYWJsZXMuIFNlZSB0aGlzIFtjYXNlIHN0dWR5XShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vb2NzLWJwLWRpZXQvKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBsaW5lYXIgcmVncmVzc2lvbi4gDQoNCmBgYHtyfQ0KbWFqb3JfZ3JvdXBzX2xvbmcgJT4lDQogIGZpbHRlcihHZW5kZXIgPT0gIkFsbCIsDQogIFJhY2VfRXRobmljaXR5ID09ICJOYXRpdmUgQW1lcmljYW4iKSAlPiUNCiAgbG0oUGVyY2VudCB+IFllYXIsIGRhdGEgPSAuKSAlPiUNCiAgc3VtbWFyeSgpDQpgYGANCg0KQmFzZWQgb24gdGhlIGNvZWZmaWNpZW50cyBpbiB0aGUgb3V0cHV0LCBmb3IgZWFjaCBvbmUgeWVhciBjaGFuZ2UsIHRoZSBtZWFuIGluY3JlYXNlIGluIGRpc2Nvbm5lY3Rpb24gcmF0ZXMgaXMNCmByIChtYWpvcl9ncm91cHNfbG9uZyU+JSBmaWx0ZXIoR2VuZGVyID09ICJBbGwiLCBSYWNlX0V0aG5pY2l0eSA9PSAiTmF0aXZlIEFtZXJpY2FuIikgJT4lIGxtKFBlcmNlbnQgfiBZZWFyLCBkYXRhID0gLikpW1siY29lZmZpY2llbnRzIl1dWyJZZWFyIl1gDQoNCldlIGNhbiBhbHNvIHNlZSBmcm9tIHRoZSAkcCQtdmFsdWUgKGBQcig+fHR8KWApLCB0aGF0IHRoaXMgcmVsYXRpb25zaGlwIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCBhcyBpdCBpcyBtdWNoIGxhcmdlciB0aGFuIDAuMDUuIEFnYWluLCB3ZSBhcmUgaG93ZXZlciBsYXJnZWx5IGxpbWl0ZWQgYnkgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gdGhpcyBkYXRhc2V0LiANCg0KV2UgY2FuIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGFib3ZlIGxpa2Ugc28gdXNpbmcgdGhlIGBnZW9tX3Ntb290aCgpYCBmdW5jdGlvbiBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2Ugd2l0aCB0aGUgYG1ldGhvZCA9ICJsbSJgIGFyZ3VtZW50IHRvIHNob3cgdGhlIGxpbmVhciBtb2RlbC4NCg0KYGBge3J9DQptYWpvcl9ncm91cHNfbG9uZyAlPiUNCiAgZmlsdGVyKEdlbmRlciA9PSAiQWxsIiwNCiAgUmFjZV9FdGhuaWNpdHkgPT0gIk5hdGl2ZSBBbWVyaWNhbiIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gUGVyY2VudCkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwOCwgMjAxOCwgYnkgPSAxKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgyMDA4LCAyMDE4LCBieSA9IDEpLA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygyMDA4LCAyMDE4KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIllvdXRoIERpc2Nvbm5lY3Rpb24gUmF0ZXMgb2YgTmF0aXZlIEFtZXJpY2FuIFlvdXRoIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDA4IC0gMjAxNyIsDQogICAgICAgeCA9ICJZZWFyIiwNCiAgICAgICB5ID0gIkRpc2Nvbm5lY3Rpb24gUmF0ZSIpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSwgdGhhdCBhbHRob3VnaCB0aGUgbGluZSBhcHBlYXJzIHRvIGhhdmUgYSBuZWdhdGl2ZSBzbG9wZSwgdGhlcmUgaXMgYSBsYXJnZSBhbW91bnQgb2YgdW5jZXJ0YWludHkgYXJvdW5kIHRoZSBmaXR0ZWQgbGluZS4gDQoNCioqKg0KDQojIyMjIEZlbWFsZSBMYXRpbnggDQoqKioNCg0KV2hhdCBhYm91dCBpZiB3ZSBsb29rIGF0IGRhdGEgdGhhdCB3ZSB0aGluayB3ZSBtaWdodCBzZWUgYSB0cmVuZCBhY2NvcmRpbmcgdG8gb3VyIGRhdGEgdmlzdWFsaXphdGlvbnMuIFRoZSBmZW1hbGUgTGF0aW54IGRhdGEgbG9va3MgYXMgdGhvdWdoIGl0IG1heSBiZSBkZWNyZWFzaW5nOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0KcGxvdF9ieV9nZW5kZXINCmBgYA0KDQpgYGB7cn0NCm1ham9yX2dyb3Vwc19sb25nICU+JQ0KICBmaWx0ZXIoR2VuZGVyID09ICJGZW1hbGUiLCBSYWNlX0V0aG5pY2l0eSA9PSAiTGF0aW54IikgJT4lDQogIHB1bGwoUGVyY2VudCkgJT4lDQogIE1hbm5LZW5kYWxsKC4pICU+JQ0KICBzdW1tYXJ5KCkNCmBgYA0KDQpJbmRlZWQgdGhpcyBkYXRhIGRvZXMgc2hvd3MgYSB0cmVuZCBvZiBkZWNsaW5pbmcgcmF0ZXMgb2YgeW91dGggZGlzY29ubmVjdGlvbiBhcyB0aGUgc2NvcmUgaXMgYWdhaW4gbmVnYXRpdmUgYW5kIGxhcmdlciBhbmQgdGhlICRwJC12YWx1ZSBpcyBub3cgIG11Y2ggbGVzcyB0aGFuIDAuNSBmb3IgZXZlbiB0aGUgMi1zaWRlZCBoeXBvdGhlc2lzLiANCg0KVGhlIFogc2NvcmUgY2FuIGJlIGNhbGN1bGF0ZWQgbGlrZSBzbzoNCg0KICRaX3tNS30gPSBcZnJhY3stMTMrMX17XHNxcnR7MjguMzMzMzN9fSA9IFxmcmFjey0xMn17NS4zMjI5MDZ9ID0gLTIuMjU0NDA4JA0KDQpUaGUgb25lIHRhaWxlZCBwLXZhbHVlIGlzIGhhbGYgb2YgdGhlIDItc2lkZWQgcC12YWx1ZSBhbmQgaXMgdGh1cyByb3VnaGx5IDAuMDEyLiBXZSBqdXN0IHRlc3RlZCB0d28gaHlwb3RoZXNlcywgb25lIGZvciB0aGUgTmF0aXZlIEFtZXJpY2FucyBhbmQgb25lIGZvciB0aGUgTGF0aW54IGZlbWFsZSBwb3B1bGF0aW9uLiBTbyB3ZSBzaG91bGQgdXNlIHRoZSBbQm9uZmVycm9uaSBtdWx0aXBsZSB0ZXN0aW5nIGNvcnJlY3Rpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0JvbmZlcnJvbmlfY29ycmVjdGlvbikgdG8gYWRqdXN0IG91ciBzaWduaWZpY2FuY2UgdGhyZXNob2xkICRcYWxwaGEkIHRvIGJlICQuMDUvKG5cdGV4dHsgdGVzdHN9KSA9IDAuNS8yID0gLjAyNSQuIEluIHRoaXMgbWV0aG9kIHdlIGRpdmlkZSBvdXIgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBhbHBoYSAoZ2VuZXJhbGx5IDAuMDUpIGJ5IHRoZSBudW1iZXIgb2YgdGVzdHMuIFdoeSBkbyB3ZSBuZWVkIHRvIGRvIHRoaXM/DQoNCkFscGhhIGlzIHRoZSBhbW91bnQgb2YgcmlzayB0aGF0IHlvdSBhcmUgd2lsbGluZyB0byBhY2NlcHQgdGhhdCBhIHN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0IGlzIGFjdHVhbGx5IGEgZmFsc2UgcG9zaXRpdmUsIGluIG90aGVyIHdvcmRzIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLiBUaGlzIGlzIGFsc28gY2FsbGVkIHR5cGUgMSBlcnJvci4gQSBzaWduaWZpY2FuY2UgdGhyZXNob2xkIG9mIC4wNSBpcyBjdXN0b21hcnkgYW5kIGl0IG1lYW5zIHRoYXQgd2UgYXJlIGFjY2VwdGluZyBhIDUlIGNoYW5jZSB0aGF0IG91ciByZXN1bHQgaXMgYWN0dWFsbHkgYSBmYWxzZSBwb3NpdGl2ZS4gV2hlbiB3ZSBwZXJmb3JtIG11bHRpcGxlIHRlc3RzLCB0aGUgcHJvYmFiaWxpdHkgb2Ygbm90IG1ha2luZyBhIHR5cGUgMSBlcnJvciB3b3VsZCByZW1haW4gdGhlIHNhbWUgZm9yIGVhY2ggdGVzdC4gQnV0IHRoZSBwcm9iYWJpbGl0eSBvdmVyYWxsIG9mIG1ha2luZyBhbiBlcnJvciBhY3Jvc3MgdGhlIHRlc3RzIGluY3JlYXNlcy4gV2UgY2FuIGNhbGN1bGF0ZSB0aGlzIGJ5IG11bHRpcGx5aW5nIHRoZSBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIHRlc3Qod2hpY2ggc3RheXMgdGhlIHNhbWUgZm9yIGVhY2ggaW5kaXZpZHVhbCB0ZXN0KS4gU2VlIFtoZXJlXShodHRwOi8vbWF0aGZvcnVtLm9yZy9saWJyYXJ5L2RybWF0aC92aWV3Lzc0MDY1Lmh0bWwpIGFib3V0IHdoeSB3ZSBtdWx0aXBseSBwcm9iYWJpbGl0aWVzIHRvZ2V0aGVyLiBUaGUgbW9yZSB0ZXN0cyB3ZSBkbywgdGhlIGxhcmdlciB0aGUgY2hhbmNlIHRoYXQgd2UgZ2V0IGEgc2lnbmlmaWNhbnQgcmVzdWx0IGluIGVycm9yLiBTZWUgW3RoaXMgY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS8pIGZvciBtb3JlIGRldGFpbHMgYWJvdXQgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uLg0KDQpPdXIgJHAkLXZhbHVlIGZvciB0aGUgb25lIHNpZGVkIHRlc3Qgd2FzIDAuMDEyIHdoaWNoIGlzIGxlc3MgdGhhbiBvdXIgYWRqdXN0ZWQgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBvZiAuMDI1IGZvciBtdWx0aXBsZSB0ZXN0aW5nLiAgVGhpcyBzdWdnZXN0cyB0aGF0IGluZGVlZCB0aGVyZSBpcyBhIGRlY3JlYXNpbmcgdHJlbmQgZm9yIHlvdXRoIGRpc2Nvbm5lY3Rpb24gYW1vbmcgdGhlIEZlbWFsZSBMYXRpbnggcG9wdWxhdGlvbi4gDQoNCioqKg0KDQojIyAqKlN1bW1hcnkqKg0KKioqIA0KDQojIyMgKipTdW1tYXJ5IFBsb3QqKg0KKioqDQoNCk5vdyB3ZSB3aWxsIGNyZWF0ZSBhIHBsb3QgdGhhdCBzdW1tYXJpemUgb3VyIGZpbmRpbmdzLiANCg0KDQpXZSB3aWxsIGNyZWF0ZSBhIHBsb3QgdGhhdCBpcyBqdXN0IGFuIGFycm93IHVzaW5nIGBnZW9tX3NlZ21lbnQoKWAuIA0KDQpUaGlzIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzIDoNCjEpIHggLSAgeCBjb29yZGluYXRlIG9mIHN0YXJ0aW5nIHBvaW50IG9mIHRoZSBhcnJvdw0KMikgeSAtIHkgY29vcmRpbmF0ZSBvZiBzdGFydGluZyBwb2ludCBvZiB0aGUgYXJyb3cNCjMpIHhlbmQgLSAgeCBjb29yZGluYXRlIG9mIGVuZCBwb2ludCBvZiB0aGUgYXJyb3cNCjQpIHllbmQgLSB5IGNvb3JkaW5hdGUgb2YgZW5kIHBvaW50IGlmIHRoZSBhcnJvdw0KDQpBbHNvIGBnZ3Bsb3RgIHJlcXVpcmVzIGEgZGF0YSBpbnB1dCwgd2Ugd2lsbCB1c2UgdGhpcyB0byBjcmVhdGUgdGhlIHkgYXhpcyB2YWx1ZSBmb3IgdGhlIGFycm93LiBXZSB3aWxsIHVzZSB0aGUgYGFlcygpYCBmdW5jdGlvbiB0byBkZWZpbmUgdGhlIGxlbmd0aCBhbmQgbG9jYXRpb24gb2YgdGhlIGFycm93Lg0KDQoNCldlIG5lZWQgdG8gdXNlIHRoZSBgYXJyb3dgIGFyZ3VtZW50IHdpdGggdGhlIGBhcnJvdygpYCBmdW5jdGlvbiB0byBjcmVhdGUgYW4gYXJyb3cuIFRoZSBgbGluZWVuZGAgYW5kIGBsaW5lam9pbmAgYXJndW1lbnRzIHNwZWNpZnkgd2hhdCBzdHlsZXMgdG8gdXNlLiBUaGUgYHNpemUoKWAgZnVuY3Rpb24gc3BlY2lmaWVzIGhvdyB0aGljayB0aGUgYXJyb3cgaXMgZGlzcGxheWVkLg0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX3NlZ21lbnQuaHRtbCkgZm9yIGRpZmZlcmVudCBhcnJvdyBzdHlsZSBvcHRpb25zLg0KDQpUaGVuIHdlIHdpbGwgdXNlIHRoZSBgeGxpbSgpYCBmdW5jdGlvbiBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2UgdG8gc3BlY2lmeSB0aGUgb3ZlcmFsbCBzaXplIG9mIHRoZSBwbG90IHJlbGF0aXZlIHRvIHRoZSBhcnJvdy4NCg0KDQpgYGB7cn0NCmFycm93X2RmIDwtIGRhdGEuZnJhbWUoIHkgPSAxKQ0KYXJyb3dfZGYNCg0KYXJyb3dwbG90PC1nZ3Bsb3QoYXJyb3dfZGYsIGFlcyh4ID0gLjUsIHkgPSAxLCB4ZW5kID0gMSwgeWVuZCA9IDEpKSArDQogICAgICAgICAgICAgICAgZ2VvbV9zZWdtZW50KGFycm93ID0gYXJyb3coKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVlbmQgPSAiYnV0dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVqb2luID0gIm1pdHJlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzKSsNCiAgICAgICAgICAgICAgICB4bGltKDAuMjUsIDEuMjUpICsgeWxpbSgwLDEuNSkrDQogIGdlb21fdGV4dCggbGFiZWwgPSAiTG9vayBhdFxuIHN1Ymdyb3VwcyIsIGFlcyh4ID0gLjc1LCB5ID0gLjgpLCBzaXplID0gNSwgZmFjZSA9ICJib2xkIikNCg0KYXJyb3dwbG90DQpgYGANCg0KDQpUaGVuIHdlIHdpbGwgdXNlIGB0aGVtZV92b2lkKClgIHJlbW92ZSAgdGhlIGJhY2tncm91bmQuDQoNCg0KYGBge3J9DQphcnJvd3Bsb3QgPC0gYXJyb3dwbG90ICsgIHRoZW1lX3ZvaWQoKQ0KYXJyb3dwbG90DQpgYGANCg0KDQoNCldlIHdpbGwgdXNlIHRoZSBgcGF0Y2h3b3JrYCBwYWNrYWdlIHRvIGRvIHNvLg0KDQpUaGlzIHBhY2thZ2UgYWxsb3dzIHVzIHRvIGNyZWF0ZSBmb3JtdWxhcyBmb3Igb3VyIHBsb3QgbGF5b3V0cy4gVGh1cyB3ZSBjYW4gdXNlIGArYCB0byBhZGQgcGxvdHMgYW5kIHBsb3QgZWxlbWVudHMgbGlrZSBgcGxvdF9zcGFjZXIoKWAgdG8gYWRkIGFuIGVtcHR5IHNwYWNlYCBhbmQgd2UgY2FuIHVzZSBgL2AgdG8gcGxhY2UgcGxvdHMgaW4gZGlmZmVyZW50IHJvd3Mgb24gdG9wIG9mIG9uZSBhbm90aGVyIGxpa2UgYSBmcmFjdGlvbi4gDQoNClRodXMgd2UgY2FuIGNvbWJpbmUgb3VyIHBsb3RzIHRoYXQgd2UgcHJldmlvdXNseSBuYW1lZCBhbmQgc2F2ZWQgYXMgb2JqZWN0cy4NCg0KDQpgYGB7cn0NCg0KeW91dGhfcGxvdCArIGFycm93cGxvdCAgKyAocGxvdF9ieV9nZW5kZXIgL2FzaWFuX3N1Ymdyb3VwX3Bsb3Rfc2ltcCkgDQpgYGANCg0KVGhlIGBwbG90X2xheW91dCgpYCBmdW5jdGlvbiBhbGxvd3MgZm9yIHNwZWNpZmljYXRpb24gb2YgdGhlIHJlbGF0aXZlIGhlaWdodHMgYW5kIHdpZHRocyBvZiB0aGUgdmFyaW91cyBwbG90IGVsZW1lbnRzLiBUaGVuLCBzaW5jZSB3ZSB3aWxsIGNyZWF0ZSB0aHJlZSBjb2x1bW5zIChkaXN0aW5ndWlzaGVkIGJ5IHRoZSBgK2Agc3ltYm9scywgd2UgbmVlZCB0byBzcGVjaWZ5IHdpZHRocyBhbmQgaGVpZ2h0cyBmb3IgYWxsIHRocmVlLikNCg0KDQpXZSBjYW4gY2hhbmdlIGFzcGVjdHMgYWJvdXQgIHRoZSBwbG90cyB1c2luZyB0aGUgYCtgIHN5bWJvbCBhbmQgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiBhcyB3ZSBoYXZlIHByZXZpb3VzbHkgdXNlZCBpdC4gIFdlIHdpbGwgYWxzbyBhZGQgYW4gb3ZlcmFsbCB0aXRsZSB1c2luZyB0aGUgYHBsb3RfYW5ub3RhdGlvbigpYCBmdW5jdGlvbi4NCg0KRmluYWxseSwgd2Ugd2lsbCB1c2UgdGhlIGBwbmcoKWAgZnVuY3Rpb24gb2YgdGhlIGBnckRldmljZXNgIHBhY2thZ2Ugd2hpY2ggaXMgYXV0b21hdGljYWxseSBsb2FkZWQgaW4gUlN0dWRpbyBzZXNzaW9ucyB0byBzYXZlIHRoZSBwbG90LiANCldlIHdpbGwgdXNlIHRoZSBgaGVyZSgpYCBmdW5jdGlvbiBvZiB0aGUgYGhlcmVgIHBhY2thZ2UsIHRvIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIHNhdmUgaXQgaW4gdGhlIGBpbWdgIGRpcmVjdG9yeSBhbmQgY2FsbCBpdCBgbWFpbnBsb3QucG5nYC4NCldlIGNhbiBhbHNvIHVzZSB0aGlzIGZ1bmN0aW9uIHRvIHNwZWNpZnkgdGhlIHJlc29sdXRpb24gd2l0aCBgcmVzYCBhbmQgaW4gZG9pbmcgc28sIHdlIG5lZWQgdG8gc2F2ZSB0aGUgaW1hZ2Ugd2l0aCBzaXplIHNwZWNpZmljYXRpb25zIHRvIG1ha2UgaXQgbGFyZ2VyLiANCg0KYGBge3IsIGNvbW1lbnQgID0gRkFMU0V9DQoNCnBsb3RfYnlfZ2VuZGVyICA8LXBsb3RfYnlfZ2VuZGVyICsgdGhlbWUoYXhpcy50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkNCmFzaWFuX3N1Ymdyb3VwX3Bsb3Rfc2ltcCAgPC1hc2lhbl9zdWJncm91cF9wbG90X3NpbXAgKyB0aGVtZShheGlzLnRleHQgPWVsZW1lbnRfdGV4dChzaXplID0gMTIpKQ0KDQpwbmcoZmlsZW5hbWUgPSBoZXJlOjpoZXJlKCJpbWciLCAibWFpbnBsb3QucG5nIiksDQogICAgcmVzID0gMzAwLCB3aWR0aCA9IDE1LCBoZWlnaHQgPSAxMCwgdW5pdHMgPSAiaW4iKQ0KICAgIHlvdXRoX3Bsb3QgICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dCA9ZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgYW5nbGUgPSA5MCkpICsNCiAgICBhcnJvd3Bsb3QgICsgDQogICAgICAocGxvdF9ieV9nZW5kZXIgLw0KICAgICAgICAgYXNpYW5fc3ViZ3JvdXBfcGxvdF9zaW1wKSAgKyANCiAgICAgIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoNC42LCAxLCA0LjY1KSkgJg0KcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkRlZXBlciBJbnNwZWN0aW9uIFJldmVhbHMgVW5leHBlY3RlZCBEaWZmZXJlbmNlcyBmb3IgWW91dGggRGlzY29ubmVjdGlvbiBBbW9uZ1xuR2VuZGVyIGFuZCBSYWNpYWwvRXRobmljIFN1Ymdyb3VwcyIsIA0KICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpKQ0KDQpkZXYub2ZmKCkNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIm1haW5wbG90LnBuZyIpKQ0KYGBgDQoNCg0KIyMjICoqU3lub3BzaXMqKg0KKioqDQoNCkluIHRoaXMgY2FzZSBzdHVkeSB3ZSBldmFsdWF0ZWQgcmF0ZXMgb2YgeW91dGggZGlzY29ubmVjdGlvbiBpbiB0aGUgVW5pdGVkIFN0YXRlcyBiZXR3ZWVuIDIwMDggYW5kIDIwMTgsIHdoaWNoIHdhcyBkZWZpbmVkIGFzIGluZGl2aWR1YWxzIGJldHdlZW4gYWdlICoqMTYgLSAyNCoqIHdobyBhcmUgKipuZWl0aGVyIHdvcmtpbmcgbm9yIGluIHNjaG9vbCoqIi4gSW4gcGFydGljdWxhciwgd2UgZm9jdXNlZCBvbiBldmFsdWF0aW5nIHNwZWNpZmljIHN1Ymdyb3VwcyB0byBpZGVudGlmeSBwb3B1bGF0aW9ucyB0aGF0IG1heSBleHBlcmllbmNlIHBhcnRpY3VsYXJseSBoaWdoIGxldmVscyBvZiBkaXNjb25uZWN0aW9uLiBXZSB1c2VkIHN1bW1hcnkgbGV2ZWwgZGF0YSBmcm9tIHR3byBbTWVhc3VyZSBvZiBBbWVyaWNhXShodHRwczovL21lYXN1cmVvZmFtZXJpY2Eub3JnLykgcmVwb3J0czogIA0KDQoxKSBUaGUgMjAxOSByZXBvcnQgY2FsbGVkIFtNYWtpbmcgdGhlIENvbm5lY3Rpb25dKGh0dHBzOi8vc3NyYy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS9tb2EvTWFraW5nJTIwdGhlJTIwQ29ubmVjdGlvbi5wZGYpICANCjIpIFRoZSAyMDIwIHJlcG9ydCBjYWxsZWQgW0EgRGVjYWRlIFVuZG9uZV0oaHR0cHM6Ly9zc3JjLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tL21vYS9BRGVjYWRlVW5kb25lLnBkZikgIA0KDQpUaGUgZGF0YSBpbiB0aGVzZSByZXBvcnRzIGNvbWVzIGZyb20gdGhlIFtBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5KEFDUyldKGh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvcHJvZ3JhbXMtc3VydmV5cy9hY3MpLg0KDQpXZSBkZW1vbnN0cmF0ZWQgaG93IHRvIGltcG9ydCBkYXRhIGZyb20gdGhlc2UgUERGcyBieSBleHRyYWN0aW5nIHRoZSB0ZXh0IGZyb20gc2NyZWVuIHNob3QgaW1hZ2VzIG9mIHRoZSB0YWJsZXMgd2VyZSB3ZXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluLiANCg0KSXQgd2FzIGlkZW50aWZpZWQgaW4gdGhlIDIwMTkgW01lYXN1cmUgb2YgQW1lcmljYSBhbm51YWwgcmVwb3J0XShodHRwczovL3NzcmMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vbW9hL01ha2luZyUyMHRoZSUyMENvbm5lY3Rpb24ucGRmKSB0aGF0IE5hdGl2ZSBBbWVyaWNhbiBwb3B1bGF0aW9ucyBwYXJ0aWN1bGFybHkgZXhwZXJpZW5jZSBoaWdoIHJhdGVzIG9mIHlvdXRoIGRpc2Nvbm5lY3Rpb24uIEluZGVlZCByb3VnaGx5IGEgcXVhcnRlciBvZiB0aGlzIHBvcHVsYXRpb24gYXBwZWFycyB0byBleHBlcmllbmNlIHlvdXRoIGRpc2Nvbm5lY3Rpb24sIHdoaWNoIGlzIG11Y2ggaGlnaGVyIHRoYW4gdGhlIG90aGVyIG1ham9yIHJhY2lhbCBncm91cHMuIEJ5IHBlcmZvcm1pbmcgdGhlIFtNYW5uLUtlbmRhbGwgdHJlbmQgdGVzdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL0tlbmRhbGwvS2VuZGFsbC5wZGYpKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5jb20vbWFubi1rZW5kYWxsLXRyZW5kLXRlc3QvKSBib3RoIG1hbnVhbGx5IGFuZCB1c2luZyB0aGUgYE1hbm5LZW5kYWxsKClgIGZ1bmN0aW9uIG9mIHRoZSBgS2VuZGFsbGAgcGFja2FnZSwgd2UgZGVtb25zdHJhdGVkIHRoYXQsIGdpdmVuIHRoZSBhdmFpbGFibGUgZGF0YSwgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGEgY29uc2lzdGVudCBwb3NpdGl2ZSBub3IgYSBjb25zaXN0ZW50IG5lZ2F0aXZlIHRyZW5kLiBUaGlzIHN1Z2dlc3RzIHRoYXQgbW9yZSBwcmV2ZW50aW9uIGFuZCByZS1lbmdhZ2VtZW50IHN0cmF0ZWdpZXMgc2hvdWxkIGVzcGVjaWFsbHkgZm9jdXMgb24gdGhpcyBwb3B1bGF0aW9uLg0KDQpCeSBwbG90dGluZyBzdWJncm91cHMgd2l0aGluIG91ciBkYXRhLCBpdCBpcyBjbGVhciB0aGF0IHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBhbW9uZyBwYXJ0aWN1bGFyIHBvcHVsYXRpb25zLiBXZSBzZWUgdGhhdCB0aGVyZSBhcmUgbXVjaCBoaWdoZXIgbGV2ZWxzIG9mIGRpc2Nvbm5lY3Rpb24gYW1vbmcgeW91dGhzIHdobyBhcmUgbWFsZSBhbmQgYmxhY2sgYXMgY29tcGFyZWQgdG8gYmxhY2sgZmVtYWxlcy4gDQoNCldlIGFsc28gc2VlIHRoYXQgeW91dGggZGlzY29ubmVjdGlvbiByYXRlcyBhbW9uZyBbSG1vbmddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0htb25nX3Blb3BsZSkgYW5kIENhbWJvZGlhbiBwb3B1bGF0aW9ucyBhcmUgdmVyeSBoaWdoLCBkZXNwaXRlIHJhdGVzIGJlaW5nIHF1aXRlIGxvdyBmb3IgdGhlIEFzaWFuIHBvcHVsYXRpb24gaW4gZ2VuZXJhbC4NCg0KT3ZlcmFsbCwgdGhpcyBjYXNlIHN0dWR5IGRlbW9uc3RyYXRlcyB0aGUgaW1wb3J0YW5jZSBvZiBsb29raW5nIGF0IHBvcHVsYXRpb25zIGF0IGRlZXBlciBsZXZlbHMuIFRoaXMgaXMgc29tZXRoaW5nIG9mIGdyZWF0IGltcG9ydGFuY2UgZmFjaW5nIHRoZSBmaWVsZCBvZiBwdWJsaWMgaGVhbHRoLiBXaGVyZSBwb3NzaWJsZSBkYXRhIHNob3VsZCBiZSBjb2xsZWN0ZWQgb24gYSB2YXJpZXR5IG9mIGRlbW9ncmFwaGljIGdyb3VwcyB0byBiZXR0ZXIgY2hhcmFjdGVyaXplIHZ1bG5lcmFibGUgcG9wdWxhdGlvbnMuDQoNCg0KIyMgKipTdWdnZXN0ZWQgSG9tZXdvcmsqKg0KKioqIA0KDQotIEZvciB0aGUgQXNpYW4gYW5kIExhdGlueCBzdWJncm91cCBiYXIgcGxvdHMgbWFkZSBhY3Jvc3MgeWVhciwgbW9kaWZ5IHRoZXNlIHBsb3RzIHRvIGNvbnNpZGVyIGdlbmRlciBkaWZmZXJlbmNlcyAoaW5zdGVhZCBvZiBhY3Jvc3MgdGltZSkuDQotIFRha2luZyB0aGUgcGxvdCB5b3UgbWFkZSBhYm92ZSwgbW9kaWZ5IHRoZSBwbG90IHRvIGZhY2V0IGFjcm9zcyB5ZWFycy4gDQotIEZpbmQgYW5vdGhlciB0YWJsZSBpbiBvbmUgb2YgdGhlIHJlcG9ydHMgdG8gaW1wb3J0IHVzaW5nIHRoZSBgbWFnaWNrYCBwYWNrYWdlIChmb3IgZXhhbXBsZSBwZXJoYXBzIHRoZSBkYXRhIGFib3V0IGRpZmZlcmVudCBzdGF0ZXMgb3ZlciB0aW1lIGluIHRoZSAyMDE5IHJlcG9ydCBjYWxsZWQgW01ha2luZyB0aGUgQ29ubmVjdGlvbl0oaHR0cHM6Ly9zc3JjLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tL21vYS9NYWtpbmclMjB0aGUlMjBDb25uZWN0aW9uLnBkZikpLiBMb29rIGZvciBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcyBieSBwbG90dGluZyB0aGUgZGF0YSBhbmQgZXZhbHVhdGluZyB3aXRoIHRoZSBNYW5uLUtlbmRhbGwgdGVzdC4gDQoNCiMjICoqQWRkaXRpb25hbCBJbmZvcm1hdGlvbioqDQoqKiogDQoNCg0KDQojIyMgKipIZWxwZnVsIExpbmtzKioNCioqKg0KDQpbUlN0dWRpb10oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2ZlYXR1cmVzLyl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbQ2hlYXRzaGVldCBvbiBSU3R1aWRvIElERV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9yc3R1ZGlvLWlkZS5wZGYpe3RhcmdldD0iX2JsYW5rIn0gIA0KW090aGVyIFJTdHVkaW8gY2hlYXRzaGVldHNdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyl7dGFyZ2V0PSJfYmxhbmsifSAgIA0KW1RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICANCg0KW1Jlc3BvbnNlIGJpYXNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1Jlc3BvbnNlX2JpYXMpICANCltDcm9zcy1TZWN0aW9uYWwgZGF0YV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ3Jvc3Mtc2VjdGlvbmFsX2RhdGE/b2xkZm9ybWF0PXRydWUpDQpbUG9wdWxhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUG9wdWxhdGlvbj9vbGRmb3JtYXQ9dHJ1ZSkNCltTYW1wbGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NhbXBsaW5nXyhzdGF0aXN0aWNzKT9vbGRmb3JtYXQ9dHJ1ZSkNCltTYW1wbGluZyBtZXRob2RzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TYW1wbGluZ18oc3RhdGlzdGljcyk/b2xkZm9ybWF0PXRydWUpe3RhcmdldD0iX2JsYW5rIn0gDQpbSW5mZXJlbmNlXShodHRwczovL3d3dy5icml0YW5uaWNhLmNvbS9zY2llbmNlL2luZmVyZW5jZS1zdGF0aXN0aWNzKSANCg0KW0FtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgKEFDUyldKGh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvcHJvZ3JhbXMtc3VydmV5cy9hY3MpICANCg0KU2VlIFtoZXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BbWVyaWNhbl9Db21tdW5pdHlfU3VydmV5KSBmb3IgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3VydmV5ICANCltNZWFzdXJlIG9mIEFtZXJpY2FdKGh0dHBzOi8vd3d3LnNzcmMub3JnL3Byb2dyYW1zL3ZpZXcvbW9hLykgICANCltTb2NpYWwgU2NpZW5jZSBSZXNlYXJjaCBDb3VuY2lsXShodHRwczovL3d3dy5zc3JjLm9yZy8pIA0KICAgDQoNCltQaXBpbmcgaW4gUl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hZ3JpdHRyL3ZpZ25ldHRlcy9tYWdyaXR0ci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQpbV3JpdGluZyBmdW5jdGlvbnNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZnVuY3Rpb25zLmh0bWwpICAgDQpBbHNvIHNlZSBbdGhpcyBjYXNlIHN0dWR5XShodHRwczovL3d3dy5vcGVuY2FzZXN0dWRpZXMub3JnL29jcy1icC12YXBpbmctY2FzZS1zdHVkeS8pe3RhcmdldD0iX2JsYW5rIn0gIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHdyaXRpbmcgZnVuY3Rpb25zLiAgIA0KW1N0cmluZyBtYW5pcHVsYXRpb24gY2hlYXRzaGVldF0oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKXt0YXJnZXQ9Il9ibGFuayJ9ICANCltUYWJsZSBmb3JtYXRzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XaWRlX2FuZF9uYXJyb3dfZGF0YSl7dGFyZ2V0PSJfYmxhbmsifQ0KDQpbUmVncmVzc2lvbl0oaHR0cHM6Ly9saW5kZWxvZXYuZ2l0aHViLmlvL3Rlc3RzLWFzLWxpbmVhci8pICAgIA0KW3NpbXBsZSBsaW5lYXIgcmVncmVzc2lvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX2xpbmVhcl9yZWdyZXNzaW9uKSAgIA0KW21vbm90b25pYyBhc3NvY2lhdGlvbl0oaHR0cHM6Ly93d3cuc3RhdGlzdGljc2hvd3RvLmNvbS9tb25vdG9uaWMtcmVsYXRpb25zaGlwLykgICANCltLZW5kYWxsIHJhbmsgY29ycmVsYXRpb24gY29lZmZpY2llbnRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0tlbmRhbGxfcmFua19jb3JyZWxhdGlvbl9jb2VmZmljaWVudD9vbGRmb3JtYXQ9dHJ1ZSkgICANCltOdWxsIGh5cG90aGVzaXNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL051bGxfaHlwb3RoZXNpcykgICANCltBbHRlcm5hdGl2ZSBoeXBvdGhlc2lzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BbHRlcm5hdGl2ZV9oeXBvdGhlc2lzKSAgIA0KW1Byb2JhYmlsaXR5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qcm9iYWJpbGl0eSkgICANCltvbmUtc2lkZWQgYW5kIHR3by1zaWRlZCBoeXBvdGhlc2VzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PbmUtX2FuZF90d28tdGFpbGVkX3Rlc3RzKSAgIA0KW05vbnBhcmFtZXRyaWNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL05vbnBhcmFtZXRyaWNfc3RhdGlzdGljcykgICAgIFtQYXJhbWV0cmljXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QYXJhbWV0cmljX3N0YXRpc3RpY3MpIA0KW3NpZ25pZmljYW5jZSB0aHJlc2hvbGRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1N0YXRpc3RpY2FsX3NpZ25pZmljYW5jZSkgIA0KW1ogc2NvcmVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1N0YW5kYXJkX3Njb3JlKSAgIA0KW1ogc2NvcmUgdGFibGVdKGh0dHBzOi8vc29jcmF0aWMub3JnL3F1ZXN0aW9ucy81OTg2YTNlMWI3MmNmZjZmZDQ4YTU0MDgpICANCltaIHNjb3JlIHRvIHAtdmFsdWUgY2FsY3VsYXRvcl0oaHR0cHM6Ly93d3cuc29jc2Npc3RhdGlzdGljcy5jb20vcHZhbHVlcy9ub3JtYWxkaXN0cmlidXRpb24uYXNweCkgICANCg0KDQpbYGdncGxvdDJgIHBhY2thZ2VdKGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcpe3RhcmdldD0iX2JsYW5rIn0gICAgDQpQbGVhc2Ugc2VlIFt0aGlzIGNhc2Ugc3R1ZHldKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtYnAtY28yLWVtaXNzaW9ucy8pICBmb3IgbW9yZSBkZXRhaWxzIG9uIHVzaW5nIGBnZ3Bsb3QyYCAgICANCltncmFtbWFyIG9mIGdyYXBoaWNzXShodHRwOi8vdml0YS5oYWQuY28ubnovcGFwZXJzL2xheWVyZWQtZ3JhbW1hci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQpbYGdncGxvdDJgIHRoZW1lc10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dndGhlbWUuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgIA0KW2BkaXJlY3RsYWJlbHNgIHBhY2thZ2UgbWV0aG9kc10oaHR0cDovL2RpcmVjdGxhYmVscy5yLWZvcmdlLnItcHJvamVjdC5vcmcvZG9jcy9pbmRleC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIA0KW0htb25nIHBlb3BsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSG1vbmdfcGVvcGxlKSAgIA0KW0ludGVyc2VjdGlvbnNdKGh0dHBzOi8vd3d3LnZveC5jb20vdGhlLWhpZ2hsaWdodC8yMDE5LzUvMjAvMTg1NDI4NDMvaW50ZXJzZWN0aW9uYWxpdHktY29uc2VydmF0aXNtLWxhdy1yYWNlLWdlbmRlci1kaXNjcmltaW5hdGlvbikgICANCg0KW01vdGl2YXRpbmcgYXJ0aWNsZSBmb3IgdGhpcyBjYXNlIHN0dWR5IGFib3V0IHlvdXRoIGRpc2Nvbm5lY3Rpb24vb3Bwb3J0dW5pdHkgeW91dGhdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzYyNDM0NDYvKQ0KDQoNClRvIGxlYXJuIG1vcmUgYWJvdXQgaW1wb3J0aW5nIGFuZCB3cmFuZ2xpbmcgUERGcyB1c2luZyB0aGUgYHBkZnRvb2xzYCBwYWNrYWdlIHNlZSB0aGlzIFtjYXNlIHN0dWR5XShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5LykgYW5kIHRoaXMgW2Nhc2Ugc3R1ZHldKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtYnAtZGlldC8pLiANCg0KVG8gbGVhcm4gbW9yZSBhYm91dCB3aGF0IHlvdSBjYW4gZG8gd2l0aCB0aGUgYG1hZ2lja2AgcGFja2FnZSBzZWUgdGhpcyBbdmluZ2V0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYWdpY2svdmlnbmV0dGVzL2ludHJvLmh0bWwpLiAgIA0KDQpUbyBsZWFybiBtb3JlIGFib3V0IHRoZSAqKk1hbm4tS2VuZGFsbCB0cmVuZCB0ZXN0Kiogc2VlDQpbaGVyZV0oaHR0cHM6Ly93d3cuc3RhdGlzdGljc2hvd3RvLmNvbS9tYW5uLWtlbmRhbGwtdHJlbmQtdGVzdC8pIGFuZCBbaGVyZV0oaHR0cHM6Ly93d3cuc3RhdGlzdGljc2hvd3RvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8wOC9NYW5uLUtlbmRhbGwtQW5hbHlzaXMtMS5wZGYpLiAgDQoNClRvIGxlYXJuIG1vcmUgYWJvdXQgaHlwb3RoZXNpcyB0ZXN0aW5nLCBzZWUgdGhpcyBbY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS8pLiAgICANCg0KDQo8dT4qKlBhY2thZ2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5OioqIDwvdT4NCg0KUGFja2FnZSAgIHwgVXNlIGluIHRoaXMgY2FzZSBzdHVkeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCi0tLS0tLS0tLS0gfC0tLS0tLS0tLS0tLS0NCltoZXJlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpe3RhcmdldD0iX2JsYW5rIn0gICAgICAgfCB0byBlYXNpbHkgbG9hZCBhbmQgc2F2ZSBkYXRhICANCltwZGZ0b29sc10oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnLykgICAgICB8IHRvIGltcG9ydCBQREYgZG9jdW1lbnRzICANClttYWdpY2tdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYWdpY2svdmlnbmV0dGVzL2ludHJvLmh0bWwjS2VybmVsX2NvbnZvbHV0aW9uKSAgICAgIHwgZm9yIGltcG9ydGluZyBpbWFnZXMgYW5kIGV4dHJhY3RpbmcgdGV4dCBmcm9tIGltYWdlcyAgDQpbdGVzc2VyYWN0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGVzc2VyYWN0L3ZpZ25ldHRlcy9pbnRyby5odG1sKSB8IGZvciBleHRyYWN0aW5nIHRleHQgZnJvbSBpbWFnZXMgd2l0aCBgbWFnaWNrYCAgIA0KW2tuaXRyXShodHRwczovL3lpaHVpLm9yZy9rbml0ci8pIHwgIGZvciBzaG93aW5nIGltYWdlcyBpbiByZXBvcnRzICAgIA0KW2RwbHlyXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBmaWx0ZXIsIHN1YnNldCwgam9pbiwgYWRkIHJvd3MgdG8sIGFuZCBtb2RpZnkgdGhlIGRhdGEgICANCltzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIG1hbmlwdWxhdGUgc3RyaW5ncyAgDQpbbWFncml0dHJdKGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHBpcGUgc2VxdWVudGlhbCBjb21tYW5kcyANClt0aWR5cl0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY2hhbmdlIHRoZSBzaGFwZSBvciBmb3JtYXQgb2YgdGliYmxlcyB0byB3aWRlIGFuZCBsb25nLCB0byBkcm9wIHJvd3Mgd2l0aCBgTkFgIHZhbHVlcywgdG8gc2VwYXJhdGUgYSBjb2x1bW4gaW50byBhZGRpdGlvbmFsIGNvbHVtbnMsIGFuZCB0byBmaWxsIG91dCB2YWx1ZXMgYmFzZWQgb24gcHJldmlvdXMgdmFsdWVzICAgDQpbdGliYmxlXShodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY3JlYXRlIHRpYmJsZXMgICAgDQpbZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBjcmVhdGUgcGxvdHMgIA0KW2RpcmVjdGxhYmVsc10oaHR0cDovL2RpcmVjdGxhYmVscy5yLWZvcmdlLnItcHJvamVjdC5vcmcvZG9jcy9pbmRleC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBhZGQgbGFiZWxzIGRpcmVjdGx5IHRvIGxpbmVzIGluIHBsb3RzICANCltjb3dwbG90XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY293cGxvdC92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFkZCBpbWFnZXMgdG8gcGxvdHMgDQpbZm9yY2F0c10oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byByZW9yZGVyIGZhY3RvciBmb3IgcGxvdA0KW2tlbmRhbGxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9LZW5kYWxsL0tlbmRhbGwucGRmKSB8IHRvIGltcGxlbWVudCB0aGUgTWFubi1LZW5kYWxsIHRyZW5kIHRlc3QgaW4gUiAgIA0KW3BhdGNod29ya10oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9wYXRjaHdvcmspIHwgdG8gY29tYmluZSBwbG90cw0KDQojIyMjIHsuZW1waGFzaXNfYmxvY2t9DQoNCldhbnQgdG8gbGVhcm4gbW9yZSBhYm91dCBob3cgdG8gcHJldmVudCBhbmQgbWl0aWdhdGUgeW91dGggZGlzY29ubmVjdGlvbj8gIA0KT3IgZG8geW91IGtub3cgeW91dGhzIHdobyBhcmUgZGlzY29ubmVjdGVkPyAgDQoNClNlZSBbdGhlIHByb2dyYW0gZGlyZWN0b3J5IGF0IHlvdXRoLmdvdl0oaHR0cHM6Ly95b3V0aC5nb3YvZXZpZGVuY2UtaW5ub3ZhdGlvbi9wcm9ncmFtLWRpcmVjdG9yeT9rZXl3b3Jkcz0mZmllbGRfcGRfZmFjdG9yc19yaXNrc190aWQ9NDEzJmZpZWxkX3BkX2ZhY3RvcnNfcHJvdGVjdGl2ZV90aWQ9QWxsKSBhbmQgW3RoaXMgcHJvZ3JhbSBsaXN0aW5nXShodHRwczovL2dvYy5tYXJ5bGFuZC5nb3Yvd3AtY29udGVudC91cGxvYWRzL3NpdGVzLzgvMjAxNS8xMC9Qcm9ncmFtLU1vZGVscy1mb3ItU2VydmluZy1PcHBvcnR1bml0eS1Zb3V0aC5wZGYpIGZvY3VzZWQgb24gTWFyeWxhbmQgYnV0IGluY2x1ZGluZyBvdGhlciBsb2NhdGlvbnMgZm9yIGxpc3RpbmdzIG9mIHByb2dyYW1zIGRlZGljYXRlZCB0byByZS1lbmdhZ2luZyBkaXNjb25uZWN0ZWQgeW91dGggb3IgcHJldmVudGluZyBkaXNjb25uZWN0aW9uLg0KDQpBbHNvLCBzZWUgW1RoZSBDZW50ZXIgZm9yIENvbW11bml0aWVzIFRoYXQgQ2FyZV0oaHR0cHM6Ly93d3cuY29tbXVuaXRpZXN0aGF0Y2FyZS5uZXQvKSBhbmQgdGhlDQpbUFJPU1BFUiBwcm9ncmFtXShodHRwczovL2V4dGVuc2lvbi5wc3UuZWR1L3Byb21vdGluZy1zY2hvb2wtY29tbXVuaXR5LXVuaXZlcnNpdHktcGFydG5lcnNoaXBzLXRvLWVuaGFuY2UtcmVzaWxpZW5jZSkgZm9yIHBhcnRpY3VsYXIgZXhhbXBsZXMuDQoNCiMjIyMNCg0KIyMjICoqU2Vzc2lvbiBJbmZvKioNCioqKg0KDQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQoqKkVzdGltYXRlIG9mIFJNYXJrZG93biBDb21waWxhdGlvbiBUaW1lOiAqKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnJtYXJrZG93bjo6OnBlcmZfdGltZXJfc3RvcCgicmVuZGVyIikNCnB0cyA9IHJtYXJrZG93bjo6OnBlcmZfdGltZXJfc3VtbWFyeSgpDQpjYXQoIkFib3V0Iiwgcm91bmQocHRzJHRpbWVbMV0vMTAwMCArIDUpLCAiLSIsIHJvdW5kKHB0cyR0aW1lWzFdLzEwMDAgKyAxNSksInNlY29uZHMiKQ0KYGBgDQoNClRoaXMgY29tcGlsYXRpb24gdGltZSB3YXMgbWVhc3VyZWQgb24gYSBQQyBtYWNoaW5lIG9wZXJhdGluZyBvbiBXaW5kb3dzIDEwLiBUaGlzIHJhbmdlIHNob3VsZCBvbmx5IGJlIHVzZWQgYXMgYW4gZXN0aW1hdGUgYXMgY29tcGlsYXRpb24gdGltZSB3aWxsIHZhcnkgd2l0aCBkaWZmZXJlbnQgbWFjaGluZXMgYW5kIG9wZXJhdGluZyBzeXN0ZW1zLiANCg0KIyMjICoqQWNrbm93bGVkZ21lbnRzKioNCioqKg0KDQpXZSB3b3VsZCBsaWtlIHRvIGFja25vd2xlZGdlIFtUYW1hciBNZW5kZWxzb25dKGh0dHBzOi8vd3d3Lmpoc3BoLmVkdS9mYWN1bHR5L2RpcmVjdG9yeS9wcm9maWxlLzE3NzAvdGFtYXItbWVuZGVsc29uKSBmb3IgYXNzaXN0aW5nIGluIGZyYW1pbmcgdGhlIG1ham9yIGRpcmVjdGlvbiBvZiB0aGUgY2FzZSBzdHVkeS4NCg0KV2Ugd291bGQgbGlrZSB0byBhY2tub3dsZWRnZSBbUWllciBNZW5nXShodHRwczovL3d3dy5vcGVuY2FzZXN0dWRpZXMub3JnL2F1dGhvcnMvcW1lbmcvKSBhbmQgW01pY2hhZWwgQnJlc2hvY2tdKGh0dHBzOi8vbWJyZXNob2NrLmdpdGh1Yi5pby8pIGZvciB0aGVpciBjb250cmlidXRpb25zIHRvIHRoaXMgY2FzZSBzdHVkeS4gDQoNCldlIHdvdWxkIGFsc28gbGlrZSB0byBhY2tub3dsZWRnZSB0aGUgW0Jsb29tYmVyZyBBbWVyaWNhbiBIZWFsdGggSW5pdGlhdGl2ZV0oaHR0cHM6Ly9hbWVyaWNhbmhlYWx0aC5qaHUuZWR1LykgZm9yIGZ1bmRpbmcgdGhpcyB3b3JrLiANCg0KDQoNCjxzY3JpcHQgdHlwZT0ndGV4dC9qYXZhc2NyaXB0JyBpZD0nY2x1c3RybWFwcycgc3JjPScvL2Nkbi5jbHVzdHJtYXBzLmNvbS9tYXBfdjIuanM/Y2w9MDgwODA4Jnc9YSZ0PXR0JmQ9TzBONl94eVZSckk5TFJhNFkyOXJsdWFJdVN1WTJrNVRkWlhMV1RqY28yVSZjbz1mZmZmZmYmY21vPTNhY2MzYSZjbW49ZmY1MzUzJmN0PTgwODA4MCc+PC9zY3JpcHQ+