1.0 Introduction

The costs of the Affordable Care Act (ACA)’s insurance premiums seem to have risen quite a bit here in Florida. I should know: I have obtained health insurance through Healthcare.gov, the health insurance marketplace established by the act, from year one, which was 2014. In this notebok, I want to show how I used Microsoft’s Transact SQL in Microsoft SQL Server to extract the relevant data, and then used Microsoft’s Power BI to visualize it.

2.0 Getting the data

The data is from the exchange’s own website. The data consists of several csv files, one for each year, from 2014 to 2019. I first created a new database called “Healthcare” (right-click on “Databases”, select “New Database…”), then I downloaded the rates data files and imported them into SQL Server (right-click the database, select “Tasks”, then “Import Flat File…” to import a csv file).


Each file has over a million records and twenty-some columns. In the interest of saving space and having the queries run faster, I only imported about half the columns from each file.

3.0 Running queries

3.1 A basic query

We can run a basic query to find out the monthly cost of the average insurance premium on a given year in the state of Florida:


The result:


Each year’s data is stored in a separate table. In order to get the average insurance premium for each year, we could write individual queries for each year, which would be repetitive, or we could somehow use another tool to “concatenate” the tables to create one huge table, then import it into SQL Server and run queries against it, but that also seems cumbersome.

3.2 Using loops in Transact-SQL

A distinguishing feature of Transact SQL are flow control statements: conditional execution (IF and ELSE statements) and loops. We can use a WHILE loop to iterate over the Rates tables:


Let’s unpack that query a bit. First, we have @Year as an int variable rather than a fixed value, and we are testing its value to decide whether to stay in the loop or not. We also have an nvarchar variable, @statement, because we are putting the entire query in a string. The reason for that is that the name of the table we are querying changes in each loop iteration: The first query will run against Healthcare.dbo.Rates_2014, the second against Healthcare.dbo.Rates_2015, and so on. Also, I added UNION ALL to each query (except the last one) to output the results from all the queries in a single table.


The variable @statement will have all six subqueries, 2014-2019, joined by UNION ALL. By using the WHILE loop we don’t have to explicitly have to write all six of them.

Here are the results of the second query:


3.3 Using loops and table variables (arrays) in Transact-SQL

I wanted to see the insurance premiums by age. We could modify the query in 3.1 to include Age.


That query will give us the 2014 insurance premiums in Florida, broken down and ordered by age.


Then of course we could embed that query in a WHILE loop like we did in 3.2 to look at the premiums by age and year.

I wanted to group the results by age groups rather than by individual ages, so that there would not be so many categories and the data was more amenable to plotting. Filtering a table by age to include records between this age and that age is not straightforward because some of the Ages are not numbers, such as ‘Family Option’, ‘65 and over’, ‘0-20’, and ‘0-14’. Also, some of the tables have different ranges than others. In some of the tables, for example, everyone 14 years old and younger gets lumped into a ‘0-14’ group, whereas in others, it is everyone 20 and younger. I came up with the following bins: 21-34, 35-44, 45-54, 55-63, and 64 and above.

Before we filter by Age, we need to come up with tables where ‘Family Option’, ‘0-20’, and ‘0-14’ have been removed, so we can run queries against those tables.


Then we can embed that table into a query that will run against it:


The table that resulted from the first query is renamed as ‘T2’, and the outer query is run against T2.

Age is an nvarchar variable, so we have to use CAST to convert it to int. Also, there’s an Age category called ‘65 and above’ (‘64 and above’ in some tables). We are only interested in the first two characters of those strings, i.e., ‘64’ or ‘65’. The code

CAST(SUBSTRING(T2.Age, 1, 2) AS int)

takes the first two characters of Age and casts them to an integer, so that we can run comparisons against age ranges, e.g., deciding whether Age 32 is both greater than 20 and smaller than 35. The query above returns the average premium paid by subscribers between 21 and 34 in the year 2014.


Then we can take that query, which returns the average premium in a single year for adults aged between 21 and 99 in Florida, and embed it into the earlier WHILE loop to iterate over the years.


The result:


3.3.1 Table variables

What we want to ultimately do is to compute the average premium paid by each the age range in each year. We have already seen how to loop over the years using WHILE. Now we will embed another loop into that one and set the age range in each case. In this other loop, we want to iterate over the age ranges: 21-34, 35-44, 45-54, 55-63, and 64 and above. We can create a table variable and use it as a lookup table: when the loop variable is 1, read the values from the first row of the table, from the second row when the loop variable is 2, and so on. Here is the lookup table:


Then we can finally place the query inside the two loops, an inner one that loops over age range and an outer one that loops over the years.


For each year, we query the @ageRangeTable lookup table and assign the resulting values to the @age_range variable, as well as the age limits to the @age_range_low and @age_range_high variables.


Then we substitute the age values and year values into the query, which is saved in @statement as a string. As before, we want the outputs to be concatenated, so we use UNION ALL after each loop iteration, for both loops. In the end, we have a really long query, which consists of 30 queries (6 years and 5 age ranges) similar to the one below, each with different values of year or/and age ranges, and each separated by a UNION ALL statement.


Here is the result:


4.0 Visualizing the data with Power BI and R

We can save the SQL Server results as csv files and use other software to visualize the data. I took the output from the second query, the yearly average insurance premiums in Florida, imported it into Power BI Desktop, and created a barplot.


The average premium has steadily increased, with the exception of 2016, when it dipped a little. The average rate in 2019 will be $407, a 52% increase of the $267 average paid in 2014.

We can also plot the results of the last query we ran on SQL Server, the average insurance premiums by age group.


Here we notice the large difference in premiums paid by people from different age groups. On any given year, the 55-63 group pays almost twice as much for health insurance as the 21-34 does. The average elderly person pays more than twice as much. In addition, if a person “moved up” from one age group to the next, she can expect a huge increase in her health insurance premiums. For example, if someone was in the “45-54” bucket in 2014 and found herself in the “55-63” bucket by 2019, she can expect her premiums to increase by 120%.

Generating those plots in Power BI did not require programming, just knowing where things are. It is not too difficult. Both of the plots are screen captures, since Power BI does not have a way to export a plot to an imge. Power BI does offer some programming capabilities, including the ability to write R code to generate plots. I wanted to find out the year-over-year (YOY) percentage change in insurance premiums. I wrote the code in the R interface provided by Power BI, which I reproduce here:

dataset <- dataset[with(dataset, order(Year)), ] # sorting by Year, ascending

premiums <- dataset$Premiums
years <- dataset$Year
years <- tail(years, length(years) -1)

# Computing the yoy % change in premiums
premiums_2014_18 <- dataset$Premiums[dataset$Year < 2019]
premiums_2015_19 <- dataset$Premiums[dataset$Year > 2014]
yoy <- (premiums_2015_19 - premiums_2014_18) / premiums_2014_18 
premiums_yoy <- data.frame(years, yoy)

# Plotting the barplot
library(ggplot2)
# Basic plot
yoy_plot <- ggplot(data = premiums_yoy) +  ggtitle("Affordable Care Act Year-Over-Year Increases in Average Premiums in Florida")
yoy_plot  <- yoy_plot  + geom_bar( aes(x = years, y= yoy), position = "dodge", stat = "identity", fill= "#02B8AB")

# Changing the look
yoy_plot + theme_bw() + theme(plot.title = element_text(size = 22, face = "bold"), axis.text = element_text(size = 20), axis.title.x = element_blank(), axis.title.y = element_blank())+ scale_y_continuous(labels = scales::percent) + expand_limits(y=c(-0.1, 0.3))


Here is the plot, also a screen grab:


The largest increase, by far, ocurred in the second year of the ACA implementation, although 2018 saw a large increase as well.

I wanted to plot the YOY changes by age range as well. Since I would be using R again anyway, I just did it right here in this R notebook.

premiums_by_age <- read.csv("query_4_result.csv", header = FALSE)
premiums_by_age[1,1] <- 2014 # SQL Server puts a weird character in the first item
colnames(premiums_by_age) <- c("Year", "State", "Premiums", "Age_Range")
premiums <- premiums_by_age$Premiums
premiums_by_age$Year <- as.integer(as.vector(premiums_by_age$Year))
premiums_2014_18 <- premiums_by_age$Premiums[premiums_by_age$Year < 2019]
premiums_2015_19 <- premiums_by_age$Premiums[premiums_by_age$Year > 2014]
yoy <- (premiums_2015_19 - premiums_2014_18) / premiums_2014_18 
years_2015_19 <- premiums_by_age$Year[premiums_by_age$Year > 2014]
age_range_2015_19 <- premiums_by_age$Age_Range[premiums_by_age$Year > 2014]
premiums_yoy <- data.frame(years_2015_19, age_range_2015_19, yoy)
# Plotting the barplot
library(ggplot2)
# Using a color palette similar to that of Power BI for consistency
color_blind_palette <- c("#01B7AA", "#36474C", "#FC635F", "#EDC711", "#5F6D6E")
color_fill_scheme <- scale_fill_manual(values = color_blind_palette)
# Basic plot
yoy_plot <- ggplot(data = premiums_yoy) +  ggtitle("Affordable Care Act Year-Over-Year Increases in Average Premiums in Florida, by Age") + color_fill_scheme
yoy_plot  <- yoy_plot  + geom_bar( aes(x = years_2015_19, y= yoy, fill = age_range_2015_19), position = "dodge", stat = "identity")
# Formatting the plot
yoy_plot + theme_bw() + theme(plot.title = element_text(size = 16, face = "bold"), axis.text = element_text(size = 14), axis.title.x = element_blank(), axis.title.y = element_blank(), legend.position = "top", legend.title = element_blank(), legend.text = element_text(size = 14))+ scale_y_continuous(labels = scales::percent) + expand_limits(y=c(-0.1, 0.3))


In 2015 and 2016, the older age groups saw steeper increases in their average premiums, but in the last 3 years it has been the other way around.

5.0 References

  1. Allix, G., Malcolm, G. Querying Data with Transact-SQL. Winter 2018. edX.org.

  2. Thompson, W. Analyzing and Visualizing Data with Power BI. Winter 2018. edX.org.

  3. Wenzel, K. Build Dynamic SQL in a Stored Procedure

  4. Calbimonte, D. How to implement array-like functionality in SQL Server

  5. Chang, W. Cookbook for R. Sebastopol: O’Reilly Media, 2013.

  6. Christopher DuBois and Dirk Eddelbuettel. How to sort a dataframe by multiple column(s)?.

  7. STAT and luchonacho. How to change y axis range to percent (%) from number in barplot with R.

  8. tHeSiD and Jahan Zinedine. Append Results from two queries and output as a single table.

LS0tDQp0aXRsZTogIkV4cGxvcmluZyB0aGUgcmlzaW5nIGNvc3RzIG9mIHRoZSBBZmZvcmRhYmxlIENhcmUgQWN0J3MgaW5zdXJhbmNlIHByZW1pdW1zIGluIEZsb3JpZGEiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmJvZHksIHRkIHsNCiAgIGZvbnQtc2l6ZTogMThweDsNCn0NCmgxIHsNCiAgZm9udC1zaXplOiAzMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsNCiAgZm9udC1zaXplOiAyOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgzIHsNCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmg0IHsNCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmNvZGUucnsNCiAgZm9udC1zaXplOiAxNnB4Ow0KfQ0KcHJlIHsNCiAgZm9udC1zaXplOiAxNnB4DQp9DQo8L3N0eWxlPg0KDQojIyAxLjAgSW50cm9kdWN0aW9uDQoNClRoZSBjb3N0cyBvZiB0aGUgQWZmb3JkYWJsZSBDYXJlIEFjdCAoQUNBKSdzIGluc3VyYW5jZSBwcmVtaXVtcyBzZWVtIHRvIGhhdmUgcmlzZW4gcXVpdGUgYSBiaXQgaGVyZSBpbiBGbG9yaWRhLiBJIHNob3VsZCBrbm93OiBJIGhhdmUgb2J0YWluZWQgaGVhbHRoIGluc3VyYW5jZSB0aHJvdWdoIFtIZWFsdGhjYXJlLmdvdl0oaHR0cHM6Ly93d3cuaGVhbHRoY2FyZS5nb3YvKSwgdGhlIGhlYWx0aCBpbnN1cmFuY2UgbWFya2V0cGxhY2UgZXN0YWJsaXNoZWQgYnkgdGhlIGFjdCwgZnJvbSB5ZWFyIG9uZSwgd2hpY2ggd2FzIDIwMTQuIEluIHRoaXMgbm90ZWJvaywgSSB3YW50IHRvIHNob3cgaG93IEkgdXNlZCBNaWNyb3NvZnQncyBbVHJhbnNhY3QgU1FMXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UcmFuc2FjdC1TUUwpIGluIFtNaWNyb3NvZnQgU1FMIFNlcnZlcl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWljcm9zb2Z0X1NRTF9TZXJ2ZXIpIHRvIGV4dHJhY3QgdGhlIHJlbGV2YW50IGRhdGEsIGFuZCB0aGVuIHVzZWQgTWljcm9zb2Z0J3MgW1Bvd2VyIEJJXShodHRwczovL3Bvd2VyYmkubWljcm9zb2Z0LmNvbS9lbi11cy8pIHRvIHZpc3VhbGl6ZSBpdC4NCg0KIyMgMi4wIEdldHRpbmcgdGhlIGRhdGENCg0KVGhlIGRhdGEgaXMgZnJvbSB0aGUgZXhjaGFuZ2UncyBvd24gW3dlYnNpdGVdKGh0dHBzOi8vZGF0YS5oZWFsdGhjYXJlLmdvdi8pLiBUaGUgZGF0YSBjb25zaXN0cyBvZiBzZXZlcmFsIGNzdiBmaWxlcywgb25lIGZvciBlYWNoIHllYXIsIGZyb20gMjAxNCB0byAyMDE5LiBJIGZpcnN0IGNyZWF0ZWQgYSBuZXcgZGF0YWJhc2UgY2FsbGVkICJIZWFsdGhjYXJlIiAocmlnaHQtY2xpY2sgb24gIkRhdGFiYXNlcyIsIHNlbGVjdCAiTmV3IERhdGFiYXNlLi4uIiksIHRoZW4gSSBkb3dubG9hZGVkIHRoZSByYXRlcyBkYXRhIGZpbGVzIGFuZCBpbXBvcnRlZCB0aGVtIGludG8gU1FMIFNlcnZlciAocmlnaHQtY2xpY2sgdGhlIGRhdGFiYXNlLCBzZWxlY3QgIlRhc2tzIiwgdGhlbiAiSW1wb3J0IEZsYXQgRmlsZS4uLiIgdG8gaW1wb3J0IGEgY3N2IGZpbGUpLg0KDQohW10oaGVhbHRoY2FyZV90YWJsZXMuUE5HICJIZWFsdGhjYXJlIGRhdGEgdGFibGVzIikNCjxicj4NCg0KRWFjaCBmaWxlIGhhcyBvdmVyIGEgbWlsbGlvbiByZWNvcmRzIGFuZCB0d2VudHktc29tZSBjb2x1bW5zLiBJbiB0aGUgaW50ZXJlc3Qgb2Ygc2F2aW5nIHNwYWNlIGFuZCBoYXZpbmcgdGhlIHF1ZXJpZXMgcnVuIGZhc3RlciwgSSBvbmx5IGltcG9ydGVkIGFib3V0IGhhbGYgdGhlIGNvbHVtbnMgZnJvbSBlYWNoIGZpbGUuDQoNCiMjIDMuMCBSdW5uaW5nIHF1ZXJpZXMNCg0KIyMjIDMuMSBBIGJhc2ljIHF1ZXJ5DQoNCldlIGNhbiBydW4gYSBiYXNpYyBxdWVyeSB0byBmaW5kIG91dCB0aGUgbW9udGhseSBjb3N0IG9mIHRoZSBhdmVyYWdlIGluc3VyYW5jZSBwcmVtaXVtIG9uIGEgZ2l2ZW4geWVhciBpbiB0aGUgc3RhdGUgb2YgRmxvcmlkYToNCg0KIVtdKHF1ZXJ5XzEuUE5HICJRdWVyeSAxIikNCjxicj4NCg0KVGhlIHJlc3VsdDoNCg0KIVtdKHF1ZXJ5XzFfcmVzdWx0LlBORyAiUXVlcnkgMSByZXN1bHQiKQ0KPGJyPg0KDQpFYWNoIHllYXIncyBkYXRhIGlzIHN0b3JlZCBpbiBhIHNlcGFyYXRlIHRhYmxlLiBJbiBvcmRlciB0byBnZXQgdGhlIGF2ZXJhZ2UgaW5zdXJhbmNlIHByZW1pdW0gZm9yIGVhY2ggeWVhciwgd2UgY291bGQgd3JpdGUgaW5kaXZpZHVhbCBxdWVyaWVzIGZvciBlYWNoIHllYXIsIHdoaWNoIHdvdWxkIGJlIHJlcGV0aXRpdmUsIG9yIHdlIGNvdWxkIHNvbWVob3cgdXNlIGFub3RoZXIgdG9vbCB0byAiY29uY2F0ZW5hdGUiIHRoZSB0YWJsZXMgdG8gY3JlYXRlIG9uZSBodWdlIHRhYmxlLCB0aGVuIGltcG9ydCBpdCBpbnRvIFNRTCBTZXJ2ZXIgYW5kIHJ1biBxdWVyaWVzIGFnYWluc3QgaXQsIGJ1dCB0aGF0IGFsc28gc2VlbXMgY3VtYmVyc29tZS4gDQoNCiMjIyAzLjIgVXNpbmcgbG9vcHMgaW4gVHJhbnNhY3QtU1FMDQoNCkEgZGlzdGluZ3Vpc2hpbmcgZmVhdHVyZSBvZiBUcmFuc2FjdCBTUUwgYXJlIFtmbG93IGNvbnRyb2wgc3RhdGVtZW50c10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVHJhbnNhY3QtU1FMI0Zsb3dfY29udHJvbCk6IGNvbmRpdGlvbmFsIGV4ZWN1dGlvbiAoYGBgSUZgYGAgYW5kIGBgYEVMU0VgYGAgc3RhdGVtZW50cykgYW5kIGxvb3BzLiBXZSBjYW4gdXNlIGEgW2BgYFdISUxFYGBgXShodHRwczovL3d3dy5lc3NlbnRpYWxzcWwuY29tL2J1aWxkLWR5bmFtaWMtc3FsLXN0b3JlZC1wcm9jZWR1cmUvKSBsb29wIHRvIGl0ZXJhdGUgb3ZlciB0aGUgUmF0ZXMgdGFibGVzOg0KDQohW10ocXVlcnlfMi5QTkcgIlF1ZXJ5IDIiKQ0KPGJyPg0KDQpMZXQncyB1bnBhY2sgdGhhdCBxdWVyeSBhIGJpdC4gRmlyc3QsIHdlIGhhdmUgYGBgQFllYXJgYGAgYXMgYW4gYGBgaW50YGBgIHZhcmlhYmxlIHJhdGhlciB0aGFuIGEgZml4ZWQgdmFsdWUsIGFuZCB3ZSBhcmUgdGVzdGluZyBpdHMgdmFsdWUgdG8gZGVjaWRlIHdoZXRoZXIgdG8gc3RheSBpbiB0aGUgbG9vcCBvciBub3QuIFdlIGFsc28gaGF2ZSBhbiBgYGBudmFyY2hhcmBgYCB2YXJpYWJsZSwgYGBgQHN0YXRlbWVudGBgYCwgYmVjYXVzZSB3ZSBhcmUgcHV0dGluZyB0aGUgZW50aXJlIHF1ZXJ5IGluIGEgc3RyaW5nLiBUaGUgcmVhc29uIGZvciB0aGF0IGlzIHRoYXQgdGhlIG5hbWUgb2YgdGhlIHRhYmxlIHdlIGFyZSBxdWVyeWluZyBjaGFuZ2VzIGluIGVhY2ggbG9vcCBpdGVyYXRpb246IFRoZSBmaXJzdCBxdWVyeSB3aWxsIHJ1biBhZ2FpbnN0IGBgYEhlYWx0aGNhcmUuZGJvLlJhdGVzXzIwMTRgYGAsIHRoZSBzZWNvbmQgYWdhaW5zdCBgYGBIZWFsdGhjYXJlLmRiby5SYXRlc18yMDE1YGBgLCBhbmQgc28gb24uIEFsc28sIEkgYWRkZWQgYGBgVU5JT04gQUxMYGBgIHRvIGVhY2ggcXVlcnkgKGV4Y2VwdCB0aGUgbGFzdCBvbmUpIHRvIG91dHB1dCB0aGUgcmVzdWx0cyBmcm9tIGFsbCB0aGUgcXVlcmllcyBpbiBhIHNpbmdsZSB0YWJsZS4NCg0KIVtdKHF1ZXJ5XzJfc3VicXVlcmllcy5QTkcgIlF1ZXJ5IDIgc3VicXVlcmllcyIpDQo8YnI+DQoNClRoZSB2YXJpYWJsZSBgYGBAc3RhdGVtZW50YGBgIHdpbGwgaGF2ZSBhbGwgc2l4IHN1YnF1ZXJpZXMsIDIwMTQtMjAxOSwgam9pbmVkIGJ5IGBgYFVOSU9OIEFMTGBgYC4gQnkgdXNpbmcgdGhlIGBgYFdISUxFYGBgIGxvb3Agd2UgZG9uJ3QgaGF2ZSB0byBleHBsaWNpdGx5IGhhdmUgdG8gd3JpdGUgYWxsIHNpeCBvZiB0aGVtLiANCg0KSGVyZSBhcmUgdGhlIHJlc3VsdHMgb2YgdGhlIHNlY29uZCBxdWVyeToNCg0KIVtdKHF1ZXJ5XzJfcmVzdWx0LlBORyAiUXVlcnkgMiByZXN1bHQiKQ0KPGJyPg0KDQojIyMgMy4zIFVzaW5nIGxvb3BzIGFuZCB0YWJsZSB2YXJpYWJsZXMgKGFycmF5cykgaW4gVHJhbnNhY3QtU1FMDQoNCkkgd2FudGVkIHRvIHNlZSB0aGUgaW5zdXJhbmNlIHByZW1pdW1zIGJ5IGFnZS4gV2UgY291bGQgbW9kaWZ5IHRoZSBxdWVyeSBpbiAzLjEgdG8gaW5jbHVkZSBBZ2UuDQoNCiFbXShxdWVyeV8zLlBORyAiUXVlcnkgMyIpDQo8YnI+DQoNClRoYXQgcXVlcnkgd2lsbCBnaXZlIHVzIHRoZSAyMDE0IGluc3VyYW5jZSBwcmVtaXVtcyBpbiBGbG9yaWRhLCBicm9rZW4gZG93biBhbmQgb3JkZXJlZCBieSBhZ2UuDQoNCiFbXShxdWVyeV8zX3Jlc3VsdC5QTkcgIlF1ZXJ5IDMgcmVzdWx0IikNCjxicj4NCg0KVGhlbiBvZiBjb3Vyc2Ugd2UgY291bGQgZW1iZWQgdGhhdCBxdWVyeSBpbiBhIGBgYFdISUxFYGBgIGxvb3AgbGlrZSB3ZSBkaWQgaW4gMy4yIHRvIGxvb2sgYXQgdGhlIHByZW1pdW1zIGJ5IGFnZSBhbmQgeWVhci4NCg0KSSB3YW50ZWQgdG8gZ3JvdXAgdGhlIHJlc3VsdHMgYnkgYWdlIGdyb3VwcyByYXRoZXIgdGhhbiBieSBpbmRpdmlkdWFsIGFnZXMsIHNvIHRoYXQgdGhlcmUgd291bGQgbm90IGJlIHNvIG1hbnkgY2F0ZWdvcmllcyBhbmQgdGhlIGRhdGEgd2FzIG1vcmUgYW1lbmFibGUgdG8gcGxvdHRpbmcuIEZpbHRlcmluZyBhIHRhYmxlIGJ5IGFnZSB0byBpbmNsdWRlIHJlY29yZHMgYmV0d2VlbiB0aGlzIGFnZSBhbmQgdGhhdCBhZ2UgaXMgbm90IHN0cmFpZ2h0Zm9yd2FyZCBiZWNhdXNlIHNvbWUgb2YgdGhlIEFnZXMgYXJlIG5vdCBudW1iZXJzLCBzdWNoIGFzICdGYW1pbHkgT3B0aW9uJywgJzY1IGFuZCBvdmVyJywgJzAtMjAnLCBhbmQgJzAtMTQnLiBBbHNvLCBzb21lIG9mIHRoZSB0YWJsZXMgaGF2ZSBkaWZmZXJlbnQgcmFuZ2VzIHRoYW4gb3RoZXJzLiBJbiBzb21lIG9mIHRoZSB0YWJsZXMsIGZvciBleGFtcGxlLCBldmVyeW9uZSAxNCB5ZWFycyBvbGQgYW5kIHlvdW5nZXIgZ2V0cyBsdW1wZWQgaW50byBhICcwLTE0JyBncm91cCwgd2hlcmVhcyBpbiBvdGhlcnMsIGl0IGlzIGV2ZXJ5b25lIDIwIGFuZCB5b3VuZ2VyLiBJIGNhbWUgdXAgd2l0aCB0aGUgZm9sbG93aW5nIGJpbnM6IDIxLTM0LCAzNS00NCwgNDUtNTQsIDU1LTYzLCBhbmQgNjQgYW5kIGFib3ZlLg0KDQpCZWZvcmUgd2UgZmlsdGVyIGJ5IEFnZSwgd2UgbmVlZCB0byBjb21lIHVwIHdpdGggdGFibGVzIHdoZXJlICdGYW1pbHkgT3B0aW9uJywgJzAtMjAnLCBhbmQgJzAtMTQnIGhhdmUgYmVlbiByZW1vdmVkLCBzbyB3ZSBjYW4gcnVuIHF1ZXJpZXMgYWdhaW5zdCB0aG9zZSB0YWJsZXMuDQoNCiFbXShxdWVyeV80X3N1YnRhYmxlLlBORyAiUXVlcnkgNCBzdWJ0YWJsZSIpDQo8YnI+DQoNClRoZW4gd2UgY2FuIGVtYmVkIHRoYXQgdGFibGUgaW50byBhIHF1ZXJ5IHRoYXQgd2lsbCBydW4gYWdhaW5zdCBpdDoNCg0KIVtdKHF1ZXJ5XzRfZW1iZWRkZWRfdGFibGUuUE5HICJRdWVyeSA0IGVtYmVkZGVkIHRhYmxlIikNCjxicj4NCg0KVGhlIHRhYmxlIHRoYXQgcmVzdWx0ZWQgZnJvbSB0aGUgZmlyc3QgcXVlcnkgaXMgcmVuYW1lZCBhcyAnVDInLCBhbmQgdGhlIG91dGVyIHF1ZXJ5IGlzIHJ1biBhZ2FpbnN0IFQyLg0KDQpBZ2UgaXMgYW4gYGBgbnZhcmNoYXJgYGAgdmFyaWFibGUsIHNvIHdlIGhhdmUgdG8gdXNlIGBgYENBU1RgYGAgdG8gY29udmVydCBpdCB0byBgYGBpbnRgYGAuIEFsc28sIHRoZXJlJ3MgYW4gQWdlIGNhdGVnb3J5IGNhbGxlZCAnNjUgYW5kIGFib3ZlJyAoJzY0IGFuZCBhYm92ZScgaW4gc29tZSB0YWJsZXMpLiBXZSBhcmUgb25seSBpbnRlcmVzdGVkIGluIHRoZSBmaXJzdCB0d28gY2hhcmFjdGVycyBvZiB0aG9zZSBzdHJpbmdzLCBpLmUuLCAnNjQnIG9yICc2NScuIFRoZSBjb2RlDQoNCmBgYENBU1QoU1VCU1RSSU5HKFQyLkFnZSwgMSwgMikgQVMgaW50KWBgYA0KDQp0YWtlcyB0aGUgZmlyc3QgdHdvIGNoYXJhY3RlcnMgb2YgQWdlIGFuZCBjYXN0cyB0aGVtIHRvIGFuIGludGVnZXIsIHNvIHRoYXQgd2UgY2FuIHJ1biBjb21wYXJpc29ucyBhZ2FpbnN0IGFnZSByYW5nZXMsIGUuZy4sIGRlY2lkaW5nIHdoZXRoZXIgQWdlIDMyIGlzIGJvdGggZ3JlYXRlciB0aGFuIDIwIGFuZCBzbWFsbGVyIHRoYW4gMzUuIFRoZSBxdWVyeSBhYm92ZSByZXR1cm5zIHRoZSBhdmVyYWdlIHByZW1pdW0gcGFpZCBieSBzdWJzY3JpYmVycyBiZXR3ZWVuIDIxIGFuZCAzNCBpbiB0aGUgeWVhciAyMDE0Lg0KDQohW10ocXVlcnlfNF9lbWJlZGRlZF90YWJsZV9yZXN1bHQuUE5HICJRdWVyeSA0IGVtYmVkZGVkIHRhYmxlIHJlc3VsdCIpDQo8YnI+DQoNClRoZW4gd2UgY2FuIHRha2UgdGhhdCBxdWVyeSwgd2hpY2ggcmV0dXJucyB0aGUgYXZlcmFnZSBwcmVtaXVtIGluIGEgc2luZ2xlIHllYXIgZm9yIGFkdWx0cyBhZ2VkIGJldHdlZW4gMjEgYW5kIDk5IGluIEZsb3JpZGEsIGFuZCBlbWJlZCBpdCBpbnRvIHRoZSBlYXJsaWVyIGBgYFdISUxFYGBgIGxvb3AgdG8gaXRlcmF0ZSBvdmVyIHRoZSB5ZWFycy4NCg0KIVtdKHF1ZXJ5XzRfZW1iZWRkZWRfdGFibGVfc2luZ2xlX2xvb3AuUE5HICJRdWVyeSA0IGVtYmVkZGVkIHRhYmxlIHNpbmdsZSBsb29wIikNCjxicj4NCg0KVGhlIHJlc3VsdDoNCg0KIVtdKHF1ZXJ5XzRfZW1iZWRkZWRfdGFibGVfc2luZ2xlX2xvb3BfcmVzdWx0LlBORyAiUXVlcnkgNCBlbWJlZGRlZCB0YWJsZSBzaW5nbGUgbG9vcCByZXN1bHQiKQ0KPGJyPg0KDQojIyMgMy4zLjEgVGFibGUgdmFyaWFibGVzDQoNCldoYXQgd2Ugd2FudCB0byB1bHRpbWF0ZWx5IGRvIGlzIHRvIGNvbXB1dGUgdGhlIGF2ZXJhZ2UgcHJlbWl1bSBwYWlkIGJ5IGVhY2ggdGhlIGFnZSByYW5nZSBpbiBlYWNoIHllYXIuIFdlIGhhdmUgYWxyZWFkeSBzZWVuIGhvdyB0byBsb29wIG92ZXIgdGhlIHllYXJzIHVzaW5nIGBgYFdISUxFYGBgLiBOb3cgd2Ugd2lsbCBlbWJlZCBhbm90aGVyIGxvb3AgaW50byB0aGF0IG9uZSBhbmQgc2V0IHRoZSBhZ2UgcmFuZ2UgaW4gZWFjaCBjYXNlLiBJbiB0aGlzIG90aGVyIGxvb3AsIHdlIHdhbnQgdG8gaXRlcmF0ZSBvdmVyIHRoZSBhZ2UgcmFuZ2VzOiAyMS0zNCwgMzUtNDQsIDQ1LTU0LCA1NS02MywgYW5kIDY0IGFuZCBhYm92ZS4gV2UgY2FuIGNyZWF0ZSBhIFt0YWJsZSB2YXJpYWJsZV0oaHR0cHM6Ly93d3cuc3Fsc2hhY2suY29tL2ltcGxlbWVudC1hcnJheS1saWtlLWZ1bmN0aW9uYWxpdHktc3FsLXNlcnZlci8pIGFuZCB1c2UgaXQgYXMgYSBsb29rdXAgdGFibGU6IHdoZW4gdGhlIGxvb3AgdmFyaWFibGUgaXMgMSwgcmVhZCB0aGUgdmFsdWVzIGZyb20gdGhlIGZpcnN0IHJvdyBvZiB0aGUgdGFibGUsIGZyb20gdGhlIHNlY29uZCByb3cgd2hlbiB0aGUgbG9vcCB2YXJpYWJsZSBpcyAyLCBhbmQgc28gb24uIEhlcmUgaXMgdGhlIGxvb2t1cCB0YWJsZToNCg0KIVtdKHF1ZXJ5XzRfbG9va3VwX3RhYmxlLlBORyAiUXVlcnkgNCBsb29rdXAgdGFibGUiKQ0KPGJyPg0KDQpUaGVuIHdlIGNhbiBmaW5hbGx5IHBsYWNlIHRoZSBxdWVyeSBpbnNpZGUgdGhlIHR3byBsb29wcywgYW4gaW5uZXIgb25lIHRoYXQgbG9vcHMgb3ZlciBhZ2UgcmFuZ2UgYW5kIGFuIG91dGVyIG9uZSB0aGF0IGxvb3BzIG92ZXIgdGhlIHllYXJzLg0KDQohW10ocXVlcnlfNC5QTkcgIlF1ZXJ5IDQiKQ0KPGJyPg0KDQpGb3IgZWFjaCB5ZWFyLCB3ZSBxdWVyeSB0aGUgYGBgQGFnZVJhbmdlVGFibGVgYGAgbG9va3VwIHRhYmxlIGFuZCBhc3NpZ24gdGhlIHJlc3VsdGluZyB2YWx1ZXMgdG8gdGhlIGBgYEBhZ2VfcmFuZ2VgYGAgdmFyaWFibGUsIGFzIHdlbGwgYXMgdGhlIGFnZSBsaW1pdHMgdG8gdGhlIGBgYEBhZ2VfcmFuZ2VfbG93YGBgIGFuZCBgYGBAYWdlX3JhbmdlX2hpZ2hgYGAgdmFyaWFibGVzLiANCg0KIVtdKHF1ZXJ5XzRfbG9va3VwX3RhYmxlX3F1ZXJpZXMuUE5HICJRdWVyeSA0IGxvb2t1cCB0YWJsZSBxdWVyaWVzIikNCjxicj4NCg0KVGhlbiB3ZSBzdWJzdGl0dXRlIHRoZSBhZ2UgdmFsdWVzIGFuZCB5ZWFyIHZhbHVlcyBpbnRvIHRoZSBxdWVyeSwgd2hpY2ggaXMgc2F2ZWQgaW4gYGBgQHN0YXRlbWVudGBgYCBhcyBhIHN0cmluZy4gQXMgYmVmb3JlLCB3ZSB3YW50IHRoZSBvdXRwdXRzIHRvIGJlIGNvbmNhdGVuYXRlZCwgc28gd2UgdXNlIGBgYFVOSU9OIEFMTGBgYCBhZnRlciBlYWNoIGxvb3AgaXRlcmF0aW9uLCBmb3IgYm90aCBsb29wcy4gSW4gdGhlIGVuZCwgd2UgaGF2ZSBhICpyZWFsbHkqIGxvbmcgcXVlcnksIHdoaWNoIGNvbnNpc3RzIG9mIDMwIHF1ZXJpZXMgKDYgeWVhcnMgYW5kIDUgYWdlIHJhbmdlcykgc2ltaWxhciB0byB0aGUgb25lIGJlbG93LCBlYWNoIHdpdGggZGlmZmVyZW50IHZhbHVlcyBvZiB5ZWFyIG9yL2FuZCBhZ2UgcmFuZ2VzLCBhbmQgZWFjaCBzZXBhcmF0ZWQgYnkgYSBgYGBVTklPTiBBTExgYGAgc3RhdGVtZW50Lg0KDQohW10ocXVlcnlfNF9lbWJlZGRlZF90YWJsZS5QTkcgIlF1ZXJ5IDQgZW1iZWRkZWQgdGFibGUiKQ0KPGJyPg0KDQpIZXJlIGlzIHRoZSByZXN1bHQ6DQoNCiFbXShxdWVyeV80X3Jlc3VsdC5QTkcgIlF1ZXJ5IDQgcmVzdWx0IikNCjxicj4NCg0KIyMgNC4wIFZpc3VhbGl6aW5nIHRoZSBkYXRhIHdpdGggUG93ZXIgQkkgYW5kIFINCg0KV2UgY2FuIHNhdmUgdGhlIFNRTCBTZXJ2ZXIgcmVzdWx0cyBhcyBjc3YgZmlsZXMgYW5kIHVzZSBvdGhlciBzb2Z0d2FyZSB0byB2aXN1YWxpemUgdGhlIGRhdGEuIEkgdG9vayB0aGUgb3V0cHV0IGZyb20gdGhlIHNlY29uZCBxdWVyeSwgdGhlIHllYXJseSBhdmVyYWdlIGluc3VyYW5jZSBwcmVtaXVtcyBpbiBGbG9yaWRhLCBpbXBvcnRlZCBpdCBpbnRvIFtQb3dlciBCSSBEZXNrdG9wXShodHRwczovL3Bvd2VyYmkubWljcm9zb2Z0LmNvbS9lbi11cy8pLCBhbmQgY3JlYXRlZCBhIGJhcnBsb3QuDQoNCiFbXShxdWVyeV8yX3Jlc3VsdF9Qb3dlckJJLlBORyAiVmlzdWFsaXppbmcgdGhlIGF2ZXJhZ2UgcHJlbWl1bXMgaW4gRkwiKQ0KPGJyPg0KDQpUaGUgYXZlcmFnZSBwcmVtaXVtIGhhcyBzdGVhZGlseSBpbmNyZWFzZWQsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiAyMDE2LCB3aGVuIGl0IGRpcHBlZCBhIGxpdHRsZS4gVGhlIGF2ZXJhZ2UgcmF0ZSBpbiAyMDE5IHdpbGwgYmUgXCQ0MDcsIGEgNTIlIGluY3JlYXNlIG9mIHRoZSBcJDI2NyBhdmVyYWdlIHBhaWQgaW4gMjAxNC4NCg0KV2UgY2FuIGFsc28gcGxvdCB0aGUgcmVzdWx0cyBvZiB0aGUgbGFzdCBxdWVyeSB3ZSByYW4gb24gU1FMIFNlcnZlciwgdGhlIGF2ZXJhZ2UgaW5zdXJhbmNlIHByZW1pdW1zIGJ5IGFnZSBncm91cC4NCg0KIVtdKHF1ZXJ5XzRfcmVzdWx0X1Bvd2VyQkkuUE5HICJWaXN1YWxpemluZyB0aGUgYXZlcmFnZSBwcmVtaXVtcyBieSBhZ2UgZ3JvdXAgaW4gRkwiKQ0KPGJyPg0KDQpIZXJlIHdlIG5vdGljZSB0aGUgbGFyZ2UgZGlmZmVyZW5jZSBpbiBwcmVtaXVtcyBwYWlkIGJ5IHBlb3BsZSBmcm9tIGRpZmZlcmVudCBhZ2UgZ3JvdXBzLiBPbiBhbnkgZ2l2ZW4geWVhciwgdGhlIDU1LTYzIGdyb3VwIHBheXMgYWxtb3N0IHR3aWNlIGFzIG11Y2ggZm9yIGhlYWx0aCBpbnN1cmFuY2UgYXMgdGhlIDIxLTM0IGRvZXMuIFRoZSBhdmVyYWdlIGVsZGVybHkgcGVyc29uIHBheXMgKm1vcmUqIHRoYW4gdHdpY2UgYXMgbXVjaC4gSW4gYWRkaXRpb24sIGlmIGEgcGVyc29uICJtb3ZlZCB1cCIgZnJvbSBvbmUgYWdlIGdyb3VwIHRvIHRoZSBuZXh0LCBzaGUgY2FuIGV4cGVjdCBhIGh1Z2UgaW5jcmVhc2UgaW4gaGVyIGhlYWx0aCBpbnN1cmFuY2UgcHJlbWl1bXMuIEZvciBleGFtcGxlLCBpZiBzb21lb25lIHdhcyBpbiB0aGUgIjQ1LTU0IiBidWNrZXQgaW4gMjAxNCBhbmQgZm91bmQgaGVyc2VsZiBpbiB0aGUgIjU1LTYzIiBidWNrZXQgYnkgMjAxOSwgc2hlIGNhbiBleHBlY3QgaGVyIHByZW1pdW1zIHRvIGluY3JlYXNlIGJ5ICoqMTIwJSoqLg0KDQpHZW5lcmF0aW5nIHRob3NlIHBsb3RzIGluIFBvd2VyIEJJIGRpZCBub3QgcmVxdWlyZSBwcm9ncmFtbWluZywganVzdCBrbm93aW5nIHdoZXJlIHRoaW5ncyBhcmUuIEl0IGlzIG5vdCB0b28gZGlmZmljdWx0LiBCb3RoIG9mIHRoZSBwbG90cyBhcmUgc2NyZWVuIGNhcHR1cmVzLCBzaW5jZSBQb3dlciBCSSBkb2VzIG5vdCBoYXZlIGEgd2F5IHRvIGV4cG9ydCBhIHBsb3QgdG8gYW4gaW1nZS4gUG93ZXIgQkkgZG9lcyBvZmZlciBzb21lIHByb2dyYW1taW5nIGNhcGFiaWxpdGllcywgaW5jbHVkaW5nIHRoZSBhYmlsaXR5IHRvIHdyaXRlIFIgY29kZSB0byBnZW5lcmF0ZSBwbG90cy4gSSB3YW50ZWQgdG8gZmluZCBvdXQgdGhlIHllYXItb3Zlci15ZWFyIChZT1kpIHBlcmNlbnRhZ2UgY2hhbmdlIGluIGluc3VyYW5jZSBwcmVtaXVtcy4gSSB3cm90ZSB0aGUgY29kZSBpbiB0aGUgUiBpbnRlcmZhY2UgcHJvdmlkZWQgYnkgUG93ZXIgQkksIHdoaWNoIEkgcmVwcm9kdWNlIGhlcmU6DQoNCmBgYHtyLCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQpkYXRhc2V0IDwtIGRhdGFzZXRbd2l0aChkYXRhc2V0LCBvcmRlcihZZWFyKSksIF0gIyBzb3J0aW5nIGJ5IFllYXIsIGFzY2VuZGluZw0KDQpwcmVtaXVtcyA8LSBkYXRhc2V0JFByZW1pdW1zDQp5ZWFycyA8LSBkYXRhc2V0JFllYXINCnllYXJzIDwtIHRhaWwoeWVhcnMsIGxlbmd0aCh5ZWFycykgLTEpDQoNCiMgQ29tcHV0aW5nIHRoZSB5b3kgJSBjaGFuZ2UgaW4gcHJlbWl1bXMNCnByZW1pdW1zXzIwMTRfMTggPC0gZGF0YXNldCRQcmVtaXVtc1tkYXRhc2V0JFllYXIgPCAyMDE5XQ0KcHJlbWl1bXNfMjAxNV8xOSA8LSBkYXRhc2V0JFByZW1pdW1zW2RhdGFzZXQkWWVhciA+IDIwMTRdDQp5b3kgPC0gKHByZW1pdW1zXzIwMTVfMTkgLSBwcmVtaXVtc18yMDE0XzE4KSAvIHByZW1pdW1zXzIwMTRfMTggDQpwcmVtaXVtc195b3kgPC0gZGF0YS5mcmFtZSh5ZWFycywgeW95KQ0KDQojIFBsb3R0aW5nIHRoZSBiYXJwbG90DQpsaWJyYXJ5KGdncGxvdDIpDQojIEJhc2ljIHBsb3QNCnlveV9wbG90IDwtIGdncGxvdChkYXRhID0gcHJlbWl1bXNfeW95KSArICBnZ3RpdGxlKCJBZmZvcmRhYmxlIENhcmUgQWN0IFllYXItT3Zlci1ZZWFyIEluY3JlYXNlcyBpbiBBdmVyYWdlIFByZW1pdW1zIGluIEZsb3JpZGEiKQ0KeW95X3Bsb3QgIDwtIHlveV9wbG90ICArIGdlb21fYmFyKCBhZXMoeCA9IHllYXJzLCB5PSB5b3kpLCBwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsPSAiIzAyQjhBQiIpDQoNCiMgQ2hhbmdpbmcgdGhlIGxvb2sNCnlveV9wbG90ICsgdGhlbWVfYncoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyLCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSsgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyBleHBhbmRfbGltaXRzKHk9YygtMC4xLCAwLjMpKQ0KDQoNCmBgYA0KDQo8YnI+DQoNCkhlcmUgaXMgdGhlIHBsb3QsIGFsc28gYSBzY3JlZW4gZ3JhYjoNCg0KIVtdKHF1ZXJ5XzJfcmVzdWx0X1lPWV9Qb3dlckJJLlBORyAiVmlzdWFsaXppbmcgdGhlIHllYXJseSAlIGNoYW5nZXMgaW4gYXZlcmFnZSBwcmVtaXVtcyBpbiBGTCIpDQo8YnI+DQoNClRoZSBsYXJnZXN0IGluY3JlYXNlLCBieSBmYXIsIG9jdXJyZWQgaW4gdGhlIHNlY29uZCB5ZWFyIG9mIHRoZSBBQ0EgaW1wbGVtZW50YXRpb24sIGFsdGhvdWdoIDIwMTggc2F3IGEgbGFyZ2UgaW5jcmVhc2UgYXMgd2VsbC4NCg0KSSB3YW50ZWQgdG8gcGxvdCB0aGUgWU9ZIGNoYW5nZXMgYnkgYWdlIHJhbmdlIGFzIHdlbGwuIFNpbmNlIEkgd291bGQgYmUgdXNpbmcgUiBhZ2FpbiBhbnl3YXksIEkganVzdCBkaWQgaXQgcmlnaHQgaGVyZSBpbiB0aGlzIFIgbm90ZWJvb2suDQoNCmBgYHtyLCBmaWcud2lkdGg9MTB9DQpwcmVtaXVtc19ieV9hZ2UgPC0gcmVhZC5jc3YoInF1ZXJ5XzRfcmVzdWx0LmNzdiIsIGhlYWRlciA9IEZBTFNFKQ0KcHJlbWl1bXNfYnlfYWdlWzEsMV0gPC0gMjAxNCAjIFNRTCBTZXJ2ZXIgcHV0cyBhIHdlaXJkIGNoYXJhY3RlciBpbiB0aGUgZmlyc3QgaXRlbQ0KDQpjb2xuYW1lcyhwcmVtaXVtc19ieV9hZ2UpIDwtIGMoIlllYXIiLCAiU3RhdGUiLCAiUHJlbWl1bXMiLCAiQWdlX1JhbmdlIikNCg0KcHJlbWl1bXMgPC0gcHJlbWl1bXNfYnlfYWdlJFByZW1pdW1zDQpwcmVtaXVtc19ieV9hZ2UkWWVhciA8LSBhcy5pbnRlZ2VyKGFzLnZlY3RvcihwcmVtaXVtc19ieV9hZ2UkWWVhcikpDQoNCnByZW1pdW1zXzIwMTRfMTggPC0gcHJlbWl1bXNfYnlfYWdlJFByZW1pdW1zW3ByZW1pdW1zX2J5X2FnZSRZZWFyIDwgMjAxOV0NCnByZW1pdW1zXzIwMTVfMTkgPC0gcHJlbWl1bXNfYnlfYWdlJFByZW1pdW1zW3ByZW1pdW1zX2J5X2FnZSRZZWFyID4gMjAxNF0NCnlveSA8LSAocHJlbWl1bXNfMjAxNV8xOSAtIHByZW1pdW1zXzIwMTRfMTgpIC8gcHJlbWl1bXNfMjAxNF8xOCANCnllYXJzXzIwMTVfMTkgPC0gcHJlbWl1bXNfYnlfYWdlJFllYXJbcHJlbWl1bXNfYnlfYWdlJFllYXIgPiAyMDE0XQ0KYWdlX3JhbmdlXzIwMTVfMTkgPC0gcHJlbWl1bXNfYnlfYWdlJEFnZV9SYW5nZVtwcmVtaXVtc19ieV9hZ2UkWWVhciA+IDIwMTRdDQoNCnByZW1pdW1zX3lveSA8LSBkYXRhLmZyYW1lKHllYXJzXzIwMTVfMTksIGFnZV9yYW5nZV8yMDE1XzE5LCB5b3kpDQoNCiMgUGxvdHRpbmcgdGhlIGJhcnBsb3QNCmxpYnJhcnkoZ2dwbG90MikNCiMgVXNpbmcgYSBjb2xvciBwYWxldHRlIHNpbWlsYXIgdG8gdGhhdCBvZiBQb3dlciBCSSBmb3IgY29uc2lzdGVuY3kNCmNvbG9yX2JsaW5kX3BhbGV0dGUgPC0gYygiIzAxQjdBQSIsICIjMzY0NzRDIiwgIiNGQzYzNUYiLCAiI0VEQzcxMSIsICIjNUY2RDZFIikNCmNvbG9yX2ZpbGxfc2NoZW1lIDwtIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX2JsaW5kX3BhbGV0dGUpDQoNCiMgQmFzaWMgcGxvdA0KeW95X3Bsb3QgPC0gZ2dwbG90KGRhdGEgPSBwcmVtaXVtc195b3kpICsgIGdndGl0bGUoIkFmZm9yZGFibGUgQ2FyZSBBY3QgWWVhci1PdmVyLVllYXIgSW5jcmVhc2VzIGluIEF2ZXJhZ2UgUHJlbWl1bXMgaW4gRmxvcmlkYSwgYnkgQWdlIikgKyBjb2xvcl9maWxsX3NjaGVtZQ0KeW95X3Bsb3QgIDwtIHlveV9wbG90ICArIGdlb21fYmFyKCBhZXMoeCA9IHllYXJzXzIwMTVfMTksIHk9IHlveSwgZmlsbCA9IGFnZV9yYW5nZV8yMDE1XzE5KSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikNCg0KIyBGb3JtYXR0aW5nIHRoZSBwbG90DQp5b3lfcGxvdCArIHRoZW1lX2J3KCkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkrIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgZXhwYW5kX2xpbWl0cyh5PWMoLTAuMSwgMC4zKSkNCmBgYA0KDQo8YnI+DQoNCkluIDIwMTUgYW5kIDIwMTYsIHRoZSBvbGRlciBhZ2UgZ3JvdXBzIHNhdyBzdGVlcGVyIGluY3JlYXNlcyBpbiB0aGVpciBhdmVyYWdlIHByZW1pdW1zLCBidXQgaW4gdGhlIGxhc3QgMyB5ZWFycyBpdCBoYXMgYmVlbiB0aGUgb3RoZXIgd2F5IGFyb3VuZC4NCg0KIyMgNS4wIFJlZmVyZW5jZXMNCg0KMS4gQWxsaXgsIEcuLCBNYWxjb2xtLCBHLiBbKioqUXVlcnlpbmcgRGF0YSB3aXRoIFRyYW5zYWN0LVNRTCoqKl0oaHR0cHM6Ly93d3cuZWR4Lm9yZy9jb3Vyc2UvcXVlcnlpbmctZGF0YS13aXRoLXRyYW5zYWN0LXNxbC0wKS4gV2ludGVyIDIwMTguIGVkWC5vcmcuDQoNCjIuIFRob21wc29uLCBXLiBbKioqQW5hbHl6aW5nIGFuZCBWaXN1YWxpemluZyBEYXRhIHdpdGggUG93ZXIgQkkqKipdKGh0dHBzOi8vd3d3LmVkeC5vcmcvY291cnNlL2FuYWx5emluZy1hbmQtdmlzdWFsaXppbmctZGF0YS13aXRoLXBvd2VyLWJpLTApLiBXaW50ZXIgMjAxOC4gZWRYLm9yZy4NCg0KMy4gV2VuemVsLCBLLiBbKioqQnVpbGQgRHluYW1pYyBTUUwgaW4gYSBTdG9yZWQgUHJvY2VkdXJlKioqXShodHRwczovL3d3dy5lc3NlbnRpYWxzcWwuY29tL2J1aWxkLWR5bmFtaWMtc3FsLXN0b3JlZC1wcm9jZWR1cmUvKQ0KDQo0LiBDYWxiaW1vbnRlLCBELiBbKioqSG93IHRvIGltcGxlbWVudCBhcnJheS1saWtlIGZ1bmN0aW9uYWxpdHkgaW4gU1FMIFNlcnZlcioqKl0oaHR0cHM6Ly93d3cuc3Fsc2hhY2suY29tL2ltcGxlbWVudC1hcnJheS1saWtlLWZ1bmN0aW9uYWxpdHktc3FsLXNlcnZlci8pDQoNCjUuIENoYW5nLCBXLiBbKioqQ29va2Jvb2sgZm9yIFIqKipdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vKS4gU2ViYXN0b3BvbDogTydSZWlsbHkgTWVkaWEsIDIwMTMuDQoNCjYuIENocmlzdG9waGVyIER1Qm9pcyBhbmQgRGlyayBFZGRlbGJ1ZXR0ZWwuIFsqKipIb3cgdG8gc29ydCBhIGRhdGFmcmFtZSBieSBtdWx0aXBsZSBjb2x1bW4ocyk/KioqXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMjk2NjQ2L2hvdy10by1zb3J0LWEtZGF0YWZyYW1lLWJ5LW11bHRpcGxlLWNvbHVtbnM/cnE9MSkuDQoNCjcuIFNUQVQgYW5kIGx1Y2hvbmFjaG8uIFsqKipIb3cgdG8gY2hhbmdlIHkgYXhpcyByYW5nZSB0byBwZXJjZW50ICglKSBmcm9tIG51bWJlciBpbiBiYXJwbG90IHdpdGggUioqKl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjc0MzM3OTgvaG93LXRvLWNoYW5nZS15LWF4aXMtcmFuZ2UtdG8tcGVyY2VudC1mcm9tLW51bWJlci1pbi1iYXJwbG90LXdpdGgtcikuDQoNCjguIHRIZVNpRCBhbmQgSmFoYW4gWmluZWRpbmUuIFsqKipBcHBlbmQgUmVzdWx0cyBmcm9tIHR3byBxdWVyaWVzIGFuZCBvdXRwdXQgYXMgYSBzaW5nbGUgdGFibGUqKipdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQ2MTkwOTAvYXBwZW5kLXJlc3VsdHMtZnJvbS10d28tcXVlcmllcy1hbmQtb3V0cHV0LWFzLWEtc2luZ2xlLXRhYmxlKS4NCg==