library(tidyverse)
library(openintro)
library(nycflights13)

Exercises 7.5.1.1

1. Cancelled vs Non-Cancelled Flight Departure Times

flights2 <- flights %>%
  mutate(
    cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %/% 100,
    sched_time = sched_hour + sched_min / 60
  )

p1 <- ggplot(flights2, aes(x = cancelled, y = sched_time)) +
  geom_boxplot() +
  labs(
    title = "Scheduled Departure Time by Cancellation Status",
    x = "Cancelled",
    y = "Scheduled Time (Hours)"
  )

p1

ggsave("exercise-7-5-1-1.png", plot = p1, width = 8, height = 5)

Answer:
A boxplot is a better way to compare cancelled and non-cancelled flights because it clearly shows the center and spread of scheduled departure times. Cancelled flights appear more common later in the day, which suggests delays and operational issues build up as the day goes on.

2. Most Important Predictor of Diamond Price

p2 <- ggplot(diamonds, aes(x = carat, y = price)) +
  geom_point(alpha = 0.1) +
  labs(
    title = "Price vs Carat",
    x = "Carat",
    y = "Price"
  )

p2

ggsave("exercise-7-5-1-2.png", plot = p2, width = 8, height = 5)
p2b <- ggplot(diamonds, aes(x = cut, y = carat)) +
  geom_boxplot() +
  labs(
    title = "Carat by Cut",
    x = "Cut",
    y = "Carat"
  )

p2b

ggsave("exercise-7-5-1-2b.png", plot = p2b, width = 8, height = 5)

Answer:
The most important variable for predicting the price of a diamond is carat. The scatterplot shows a strong positive relationship between carat and price, meaning larger diamonds tend to cost more.

Carat is related to cut because diamonds with lower cut quality tend to have larger carat values on average. This helps explain why lower quality diamonds can sometimes appear more expensive overall: the effect of larger size on price is stronger than the effect of cut quality alone.

3. Horizontal Boxplots using ggstance

library(ggstance)
## 
## Attaching package: 'ggstance'
## The following objects are masked from 'package:ggplot2':
## 
##     geom_errorbarh, GeomErrorbarh
p3 <- ggplot(mpg, aes(y = class, x = hwy, group = class)) +
  geom_boxploth() +
  labs(
    title = "Highway MPG by Vehicle Class",
    x = "Highway MPG",
    y = "Class"
  )

p3
## Warning: The following aesthetics were dropped during statistical transformation: x.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?
## Warning: Using the `size` aesthetic with geom_segment was deprecated in ggplot2 3.4.0.
## ℹ Please use the `linewidth` aesthetic instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Using the `size` aesthetic with geom_polygon was deprecated in ggplot2 3.4.0.
## ℹ Please use the `linewidth` aesthetic instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

ggsave("exercise-7-5-1-3.png", plot = p3, width = 8, height = 5)
## Warning: The following aesthetics were dropped during statistical transformation: x.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?
p3_flip <- ggplot(mpg, aes(x = class, y = hwy)) +
  geom_boxplot() +
  coord_flip() +
  labs(
    title = "Highway MPG by Vehicle Class (Using coord_flip)",
    x = "Class",
    y = "Highway MPG"
  )

p3_flip

ggsave("exercise-7-5-1-3b.png", plot = p3_flip, width = 8, height = 5)

Answer: The ggstance package creates a horizontal boxplot directly using geom_boxploth(), while coord_flip() starts with a regular vertical boxplot and then flips the axes. The two plots look very similar, but ggstance is more direct because it is designed specifically for horizontal geoms. In contrast, coord_flip() is a convenient way to rotate an existing plot without changing the underlying geom.

4. Letter Value Plot (geom_lv)

library(lvplot)
## 
## Attaching package: 'lvplot'
## The following object is masked from 'package:openintro':
## 
##     census
p4 <- ggplot(diamonds, aes(x = cut, y = price)) +
  geom_lv() +
  labs(
    title = "Diamond Price by Cut (Letter Value Plot)",
    x = "Cut",
    y = "Price"
  )

p4

ggsave("exercise-7-5-1-4.png", plot = p4, width = 8, height = 5)

Answer:
The letter-value plot shows the distribution of diamond prices across cut categories using more quantiles than a standard boxplot. This makes it especially useful for large datasets like diamonds, where a regular boxplot can display too many outliers and become cluttered.

The plot suggests that diamond prices vary widely within each cut category. It also shows that lower cut categories can still have higher prices, likely because larger diamonds tend to have lower cut grades, and carat has a strong influence on price. I interpret the letter-value plot as a more detailed version of a boxplot that better represents the shape and spread of large datasets. ``

5. Comparing Visualization Methods

p5_violin <- ggplot(diamonds, aes(x = cut, y = price)) +
  geom_violin() +
  labs(
    title = "Diamond Price by Cut (Violin Plot)",
    x = "Cut",
    y = "Price"
  )

p5_violin

ggsave("exercise-7-5-1-5a.png", plot = p5_violin, width = 8, height = 5)
p5_hist <- ggplot(diamonds, aes(x = price)) +
  geom_histogram(binwidth = 500) +
  facet_wrap(~cut) +
  labs(
    title = "Diamond Price by Cut (Faceted Histogram)",
    x = "Price",
    y = "Count"
  )

p5_hist

ggsave("exercise-7-5-1-5b.png", plot = p5_hist, width = 8, height = 5)
p5_freq <- ggplot(diamonds, aes(x = price, color = cut)) +
  geom_freqpoly(binwidth = 500) +
  labs(
    title = "Diamond Price by Cut (Frequency Polygon)",
    x = "Price",
    y = "Count",
    color = "Cut"
  )

p5_freq

ggsave("exercise-7-5-1-5c.png", plot = p5_freq, width = 8, height = 5)

Answer: geom_violin() is useful because it shows the overall shape and density of the distribution for each group in a compact way. It is especially helpful for comparing how spread out values are across categories. However, because it uses smoothing, it may hide some of the finer details of the raw distribution.

A faceted geom_histogram() is easier to interpret because it shows the actual distribution of values within each category. It makes it easier to see peaks, skewness, and gaps in the data. The downside is that it takes up more space and can be harder to compare groups directly across separate panels.

A colored geom_freqpoly() is good for comparing multiple groups on the same axes. It allows direct comparison of trends across categories without using separate panels. However, the lines can overlap and become cluttered, especially when there are many groups or large differences in counts.

Violin plots are compact and show shape well, faceted histograms are the most detailed and easiest to interpret, and frequency polygons are useful for comparison but can become visually messy.

6. Jitter Alternatives with ggbeeswarm

library(ggbeeswarm)
p6 <- ggplot(mpg, aes(x = class, y = hwy)) +
  geom_beeswarm() +
  labs(
    title = "Highway MPG by Vehicle Class (Beeswarm Plot)",
    x = "Class",
    y = "Highway MPG"
  )

p6

ggsave("exercise-7-5-1-6.png", plot = p6, width = 8, height = 5)

Answer:
The ggbeeswarm package provides several methods similar to geom_jitter() that help reduce overplotting while still showing individual observations.

  • geom_beeswarm() arranges points to avoid overlap by spreading them out in a controlled way, making the distribution easier to see.
  • geom_quasirandom() adds small offsets to points using a quasirandom approach, so the points are spread out but still reflect the shape of the distribution.
  • geom_sina() spreads points based on local density, so wider sections of the plot represent areas with more observations.

Compared with geom_jitter(), these methods do a better job of preserving the structure of the data while still preventing points from overlapping.

Exercises 7.5.2.1

1. Rescaling Counts to Proportions

cut_color <- diamonds %>%
  count(color, cut)

cut_color
## # A tibble: 35 × 3
##    color cut           n
##    <ord> <ord>     <int>
##  1 D     Fair        163
##  2 D     Good        662
##  3 D     Very Good  1513
##  4 D     Premium    1603
##  5 D     Ideal      2834
##  6 E     Fair        224
##  7 E     Good        933
##  8 E     Very Good  2400
##  9 E     Premium    2337
## 10 E     Ideal      3903
## # ℹ 25 more rows
cut_within_color <- cut_color %>%
  group_by(color) %>%
  mutate(prop = n / sum(n))

cut_within_color
## # A tibble: 35 × 4
## # Groups:   color [7]
##    color cut           n   prop
##    <ord> <ord>     <int>  <dbl>
##  1 D     Fair        163 0.0241
##  2 D     Good        662 0.0977
##  3 D     Very Good  1513 0.223 
##  4 D     Premium    1603 0.237 
##  5 D     Ideal      2834 0.418 
##  6 E     Fair        224 0.0229
##  7 E     Good        933 0.0952
##  8 E     Very Good  2400 0.245 
##  9 E     Premium    2337 0.239 
## 10 E     Ideal      3903 0.398 
## # ℹ 25 more rows
p721_1a <- ggplot(cut_within_color, aes(x = color, y = cut, fill = prop)) +
  geom_tile() +
  labs(
    title = "Distribution of Cut Within Color",
    x = "Color",
    y = "Cut",
    fill = "Proportion"
  )

p721_1a

ggsave("exercise-7-5-2-1-1a.png", plot = p721_1a, width = 8, height = 5)
color_within_cut <- cut_color %>%
  group_by(cut) %>%
  mutate(prop = n / sum(n))

color_within_cut
## # A tibble: 35 × 4
## # Groups:   cut [5]
##    color cut           n  prop
##    <ord> <ord>     <int> <dbl>
##  1 D     Fair        163 0.101
##  2 D     Good        662 0.135
##  3 D     Very Good  1513 0.125
##  4 D     Premium    1603 0.116
##  5 D     Ideal      2834 0.132
##  6 E     Fair        224 0.139
##  7 E     Good        933 0.190
##  8 E     Very Good  2400 0.199
##  9 E     Premium    2337 0.169
## 10 E     Ideal      3903 0.181
## # ℹ 25 more rows
p721_1b <- ggplot(color_within_cut, aes(x = color, y = cut, fill = prop)) +
  geom_tile() +
  labs(
    title = "Distribution of Color Within Cut",
    x = "Color",
    y = "Cut",
    fill = "Proportion"
  )

p721_1b

ggsave("exercise-7-5-2-1-1b.png", plot = p721_1b, width = 8, height = 5)

Answer:
The count dataset can be rescaled by converting raw counts into proportions within each group. To show the distribution of cut within colour, each count should be divided by the total count for that colour. To show the distribution of colour within cut, each count should be divided by the total count for that cut. This makes the patterns easier to compare because the values are standardized instead of being driven by group size.

2. Average Flight Delays by Detination and Month

delay_by_dest_month <- flights %>%
  mutate(total_delay = dep_delay + arr_delay) %>%
  filter(!is.na(total_delay)) %>%
  group_by(month, dest) %>%
  summarise(avg_delay = mean(total_delay), .groups = "drop")

delay_by_dest_month
## # A tibble: 1,112 × 3
##    month dest  avg_delay
##    <int> <chr>     <dbl>
##  1     1 ALB       76.6 
##  2     1 ATL        8.57
##  3     1 AUS       19.0 
##  4     1 AVL       49   
##  5     1 BDL       32.1 
##  6     1 BHM       47.0 
##  7     1 BNA       25.9 
##  8     1 BOS        2.70
##  9     1 BQN        8.52
## 10     1 BTV       18.4 
## # ℹ 1,102 more rows
p721_2 <- ggplot(delay_by_dest_month, aes(x = month, y = dest, fill = avg_delay)) +
  geom_tile() +
  labs(
    title = "Average Flight Delay by Destination and Month",
    x = "Month",
    y = "Destination",
    fill = "Average Delay"
  )

p721_2

ggsave("exercise-7-5-2-1-2.png", plot = p721_2, width = 8, height = 8)
delay_by_dest_month2 <- delay_by_dest_month %>%
  group_by(dest) %>%
  mutate(overall_avg = mean(avg_delay)) %>%
  ungroup()

p721_2_improved <- ggplot(
  delay_by_dest_month2,
  aes(x = month, y = reorder(dest, overall_avg), fill = avg_delay)
) +
  geom_tile() +
  labs(
    title = "Average Flight Delay by Destination and Month (Reordered)",
    x = "Month",
    y = "Destination",
    fill = "Average Delay"
  )

p721_2_improved

ggsave("exercise-7-5-2-1-2-improved.png", plot = p721_2_improved, width = 8, height = 8)

Answer:
The tile plot shows how average flight delays vary by destination and month, but it is difficult to read because there are many destinations and the y-axis becomes crowded. The large number of destination labels makes it hard to compare patterns clearly. The plot can be improved by reordering destinations, filtering to only a subset of destinations, or using faceting or a different visualization that reduces clutter. A reordered heatmap makes the overall pattern easier to interpret.

3. Axis Choice in Tile Plots

Answer:
It is slightly better to use aes(x = color, y = cut) because color has more categories than cut, and placing the variable with more levels on the x-axis generally makes the plot easier to read. The x-axis labels are often easier to scan, while the y-axis works well for the variable with fewer levels. Since cut has fewer categories, putting it on the y-axis creates a cleaner and more balanced display

Exercises 7.5.3.1

1. cut_width vs cut_number

diamonds_width <- diamonds %>%
  mutate(price_group = cut_width(price, width = 5000))

p731_1a <- ggplot(diamonds_width, aes(x = carat, color = price_group)) +
  geom_freqpoly(binwidth = 0.1) +
  labs(
    title = "Carat Distribution by Price Group (cut_width)",
    x = "Carat",
    y = "Count",
    color = "Price Group"
  )

p731_1a

ggsave("exercise-7-5-3-1-1a.png", plot = p731_1a, width = 8, height = 5)
diamonds_number <- diamonds %>%
  mutate(price_group = cut_number(price, n = 6))

p731_1b <- ggplot(diamonds_number, aes(x = carat, color = price_group)) +
  geom_freqpoly(binwidth = 0.1) +
  labs(
    title = "Carat Distribution by Price Group (cut_number)",
    x = "Carat",
    y = "Count",
    color = "Price Group"
  )

p731_1b

ggsave("exercise-7-5-3-1-1b.png", plot = p731_1b, width = 8, height = 5)

Answer:
When using a frequency polygon to summarize the conditional distribution, it is important to think about how the continuous variable is being grouped. cut_width() creates bins with equal numeric width, while cut_number() creates bins with roughly equal numbers of observations.

With cut_width(), the groups are easier to interpret because each bin represents the same range of price values, but some groups may contain very different numbers of diamonds. With cut_number(), the group sizes are more balanced, which can make the comparison of distributions easier, but the price ranges are no longer equally spaced.

This affects a visualization of the 2d distribution of carat and price because the apparent patterns can change depending on how the groups are formed. Equal-width bins emphasize the actual scale of price, while equal-count bins emphasize comparability between groups.

2. Carat Distribution by Price

diamonds_price_groups <- diamonds %>%
  mutate(price_group = cut_number(price, n = 6))

p731_2 <- ggplot(diamonds_price_groups, aes(x = carat, color = price_group)) +
  geom_freqpoly(binwidth = 0.1) +
  labs(
    title = "Distribution of Carat Partitioned by Price",
    x = "Carat",
    y = "Count",
    color = "Price Group"
  )

p731_2

ggsave("exercise-7-5-3-1-2.png", plot = p731_2, width = 8, height = 5)

Answer:
The frequency polygon shows that higher price groups tend to be associated with larger carat values. Lower price groups are concentrated at smaller carat sizes, while higher price groups shift to the right. This suggests a strong positive relationship between carat and price.

3. Price Comparison by Diamond Size

diamonds_size_compare <- diamonds %>%
  mutate(size_group = case_when(
    carat < 1 ~ "Small (<1 carat)",
    carat >= 2.5 ~ "Very large (>=2.5 carats)",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(size_group))

p731_3 <- ggplot(diamonds_size_compare, aes(x = price, y = after_stat(density), color = size_group)) +
  geom_freqpoly(binwidth = 500) +
  labs(
    title = "Price Distribution: Small vs Very Large Diamonds",
    x = "Price",
    y = "Density",
    color = "Size Group"
  )

p731_3

ggsave("exercise-7-5-3-1-3.png", plot = p731_3, width = 8, height = 5)

Answer: Very large diamonds have a price distribution that is shifted much farther to the right than small diamonds, meaning they are generally much more expensive. This is what I would expect, since larger diamonds are rarer and carat has a strong positive relationship with price. Using density instead of count makes the comparison clearer because it shows the shape of the price distributions without letting the much larger number of small diamonds dominate the graph.

4. Combined Visualization of Cut, Carat, and Price

diamonds_carat_groups <- diamonds %>%
  filter(carat > 0, carat <= 3) %>%
  mutate(carat_group = cut_width(carat, width = 0.5))

p731_4 <- ggplot(diamonds_carat_groups, aes(x = cut, y = price)) +
  geom_boxplot() +
  facet_wrap(~carat_group) +
  labs(
    title = "Price by Cut, Faceted by Carat Group",
    x = "Cut",
    y = "Price"
  )

p731_4

ggsave("exercise-7-5-3-1-4.png", plot = p731_4, width = 12, height = 8)

Answer:
This plot combines boxplots and faceting to show the joint distribution of cut, carat, and price. The boxplots compare price across cut categories, while the facets separate diamonds into carat groups. This makes it easier to see how price varies by cut when diamonds are grouped into similar carat ranges. The plot also shows that price generally increases as carat increases. Overall, carat appears to have a strong effect on price, and the influence of cut becomes easier to interpret when diamonds are compared within similar carat ranges.

5. Why Scatterplots Reveal 2D Outliers

p731_5 <- ggplot(diamonds, aes(x = x, y = y)) +
  geom_point() +
  coord_cartesian(xlim = c(4, 11), ylim = c(4, 11)) +
  labs(
    title = "Scatterplot of Diamond x and y Dimensions",
    x = "x",
    y = "y"
  )

p731_5

ggsave("exercise-7-5-3-1-5.png", plot = p731_5, width = 8, height = 5)

Answer: A scatterplot is a better display in this case because it shows each observation individually and makes unusual combinations of x and y easier to detect. Some diamonds have x and y values that look normal when examined separately, but together they do not follow the main pattern of the data. A binned plot would group nearby observations into tiles or bins, which could smooth over those unusual points and make them harder to identify. Because the goal is to find outliers in the two-dimensional relationship, the scatterplot is more informative. `` …

LS0tCnRpdGxlOiAiV2VlayA0IEhvbWV3b3JrIC0gQ2hhcHRlciA3IEV4ZXJjaXNlcyIKYXV0aG9yOiAiQ2FpdGxpbiBLZW5uZWR5IgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0Ci0tLQoKYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3BlbmludHJvKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKYGBgCgojIyBFeGVyY2lzZXMgNy41LjEuMQoKIyMjIDEuIENhbmNlbGxlZCB2cyBOb24tQ2FuY2VsbGVkIEZsaWdodCBEZXBhcnR1cmUgVGltZXMKCmBgYHtyfQpmbGlnaHRzMiA8LSBmbGlnaHRzICU+JQogIG11dGF0ZSgKICAgIGNhbmNlbGxlZCA9IGlzLm5hKGRlcF90aW1lKSwKICAgIHNjaGVkX2hvdXIgPSBzY2hlZF9kZXBfdGltZSAlLyUgMTAwLAogICAgc2NoZWRfbWluID0gc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCwKICAgIHNjaGVkX3RpbWUgPSBzY2hlZF9ob3VyICsgc2NoZWRfbWluIC8gNjAKICApCgpwMSA8LSBnZ3Bsb3QoZmxpZ2h0czIsIGFlcyh4ID0gY2FuY2VsbGVkLCB5ID0gc2NoZWRfdGltZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlNjaGVkdWxlZCBEZXBhcnR1cmUgVGltZSBieSBDYW5jZWxsYXRpb24gU3RhdHVzIiwKICAgIHggPSAiQ2FuY2VsbGVkIiwKICAgIHkgPSAiU2NoZWR1bGVkIFRpbWUgKEhvdXJzKSIKICApCgpwMQpgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0xLTEucG5nIiwgcGxvdCA9IHAxLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKKipBbnN3ZXI6KiogIApBIGJveHBsb3QgaXMgYSBiZXR0ZXIgd2F5IHRvIGNvbXBhcmUgY2FuY2VsbGVkIGFuZCBub24tY2FuY2VsbGVkIGZsaWdodHMgYmVjYXVzZSBpdCBjbGVhcmx5IHNob3dzIHRoZSBjZW50ZXIgYW5kIHNwcmVhZCBvZiBzY2hlZHVsZWQgZGVwYXJ0dXJlIHRpbWVzLiBDYW5jZWxsZWQgZmxpZ2h0cyBhcHBlYXIgbW9yZSBjb21tb24gbGF0ZXIgaW4gdGhlIGRheSwgd2hpY2ggc3VnZ2VzdHMgZGVsYXlzIGFuZCBvcGVyYXRpb25hbCBpc3N1ZXMgYnVpbGQgdXAgYXMgdGhlIGRheSBnb2VzIG9uLgoKIyMjIDIuIE1vc3QgSW1wb3J0YW50IFByZWRpY3RvciBvZiBEaWFtb25kIFByaWNlCgpgYGB7cn0KcDIgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQcmljZSB2cyBDYXJhdCIsCiAgICB4ID0gIkNhcmF0IiwKICAgIHkgPSAiUHJpY2UiCiAgKQoKcDIKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMS0yLnBuZyIsIHBsb3QgPSBwMiwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpwMmIgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGN1dCwgeSA9IGNhcmF0KSkgKwogIGdlb21fYm94cGxvdCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiQ2FyYXQgYnkgQ3V0IiwKICAgIHggPSAiQ3V0IiwKICAgIHkgPSAiQ2FyYXQiCiAgKQoKcDJiCmBgYAoKYGBge3J9Cmdnc2F2ZSgiZXhlcmNpc2UtNy01LTEtMmIucG5nIiwgcGxvdCA9IHAyYiwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCioqQW5zd2VyOioqICAKVGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlIGZvciBwcmVkaWN0aW5nIHRoZSBwcmljZSBvZiBhIGRpYW1vbmQgaXMgKipjYXJhdCoqLiBUaGUgc2NhdHRlcnBsb3Qgc2hvd3MgYSBzdHJvbmcgcG9zaXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gY2FyYXQgYW5kIHByaWNlLCBtZWFuaW5nIGxhcmdlciBkaWFtb25kcyB0ZW5kIHRvIGNvc3QgbW9yZS4KCkNhcmF0IGlzIHJlbGF0ZWQgdG8gY3V0IGJlY2F1c2UgZGlhbW9uZHMgd2l0aCBsb3dlciBjdXQgcXVhbGl0eSB0ZW5kIHRvIGhhdmUgbGFyZ2VyIGNhcmF0IHZhbHVlcyBvbiBhdmVyYWdlLiBUaGlzIGhlbHBzIGV4cGxhaW4gd2h5IGxvd2VyIHF1YWxpdHkgZGlhbW9uZHMgY2FuIHNvbWV0aW1lcyBhcHBlYXIgbW9yZSBleHBlbnNpdmUgb3ZlcmFsbDogdGhlIGVmZmVjdCBvZiBsYXJnZXIgc2l6ZSBvbiBwcmljZSBpcyBzdHJvbmdlciB0aGFuIHRoZSBlZmZlY3Qgb2YgY3V0IHF1YWxpdHkgYWxvbmUuCgojIyMgMy4gSG9yaXpvbnRhbCBCb3hwbG90cyB1c2luZyBnZ3N0YW5jZQoKYGBge3J9CmxpYnJhcnkoZ2dzdGFuY2UpCmBgYAoKYGBge3J9CnAzIDwtIGdncGxvdChtcGcsIGFlcyh5ID0gY2xhc3MsIHggPSBod3ksIGdyb3VwID0gY2xhc3MpKSArCiAgZ2VvbV9ib3hwbG90aCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiSGlnaHdheSBNUEcgYnkgVmVoaWNsZSBDbGFzcyIsCiAgICB4ID0gIkhpZ2h3YXkgTVBHIiwKICAgIHkgPSAiQ2xhc3MiCiAgKQoKcDMKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMS0zLnBuZyIsIHBsb3QgPSBwMywgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpwM19mbGlwIDwtIGdncGxvdChtcGcsIGFlcyh4ID0gY2xhc3MsIHkgPSBod3kpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkhpZ2h3YXkgTVBHIGJ5IFZlaGljbGUgQ2xhc3MgKFVzaW5nIGNvb3JkX2ZsaXApIiwKICAgIHggPSAiQ2xhc3MiLAogICAgeSA9ICJIaWdod2F5IE1QRyIKICApCgpwM19mbGlwCmBgYAoKYGBge3J9Cmdnc2F2ZSgiZXhlcmNpc2UtNy01LTEtM2IucG5nIiwgcGxvdCA9IHAzX2ZsaXAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgoqKkFuc3dlcjoqKgpUaGUgYGdnc3RhbmNlYCBwYWNrYWdlIGNyZWF0ZXMgYSBob3Jpem9udGFsIGJveHBsb3QgZGlyZWN0bHkgdXNpbmcgYGdlb21fYm94cGxvdGgoKWAsIHdoaWxlIGBjb29yZF9mbGlwKClgIHN0YXJ0cyB3aXRoIGEgcmVndWxhciB2ZXJ0aWNhbCBib3hwbG90IGFuZCB0aGVuIGZsaXBzIHRoZSBheGVzLiBUaGUgdHdvIHBsb3RzIGxvb2sgdmVyeSBzaW1pbGFyLCBidXQgYGdnc3RhbmNlYCBpcyBtb3JlIGRpcmVjdCBiZWNhdXNlIGl0IGlzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IgaG9yaXpvbnRhbCBnZW9tcy4gSW4gY29udHJhc3QsIGBjb29yZF9mbGlwKClgIGlzIGEgY29udmVuaWVudCB3YXkgdG8gcm90YXRlIGFuIGV4aXN0aW5nIHBsb3Qgd2l0aG91dCBjaGFuZ2luZyB0aGUgdW5kZXJseWluZyBnZW9tLgoKIyMjIDQuIExldHRlciBWYWx1ZSBQbG90IChnZW9tX2x2KQoKYGBge3J9CmxpYnJhcnkobHZwbG90KQpgYGAKCmBgYHtyfQoKcDQgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGN1dCwgeSA9IHByaWNlKSkgKwogIGdlb21fbHYoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpYW1vbmQgUHJpY2UgYnkgQ3V0IChMZXR0ZXIgVmFsdWUgUGxvdCkiLAogICAgeCA9ICJDdXQiLAogICAgeSA9ICJQcmljZSIKICApCgpwNApgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0xLTQucG5nIiwgcGxvdCA9IHA0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKCioqQW5zd2VyOioqICAKVGhlIGxldHRlci12YWx1ZSBwbG90IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgZGlhbW9uZCBwcmljZXMgYWNyb3NzIGN1dCBjYXRlZ29yaWVzIHVzaW5nIG1vcmUgcXVhbnRpbGVzIHRoYW4gYSBzdGFuZGFyZCBib3hwbG90LiBUaGlzIG1ha2VzIGl0IGVzcGVjaWFsbHkgdXNlZnVsIGZvciBsYXJnZSBkYXRhc2V0cyBsaWtlIGBkaWFtb25kc2AsIHdoZXJlIGEgcmVndWxhciBib3hwbG90IGNhbiBkaXNwbGF5IHRvbyBtYW55IG91dGxpZXJzIGFuZCBiZWNvbWUgY2x1dHRlcmVkLgoKVGhlIHBsb3Qgc3VnZ2VzdHMgdGhhdCBkaWFtb25kIHByaWNlcyB2YXJ5IHdpZGVseSB3aXRoaW4gZWFjaCBjdXQgY2F0ZWdvcnkuIEl0IGFsc28gc2hvd3MgdGhhdCBsb3dlciBjdXQgY2F0ZWdvcmllcyBjYW4gc3RpbGwgaGF2ZSBoaWdoZXIgcHJpY2VzLCBsaWtlbHkgYmVjYXVzZSBsYXJnZXIgZGlhbW9uZHMgdGVuZCB0byBoYXZlIGxvd2VyIGN1dCBncmFkZXMsIGFuZCBjYXJhdCBoYXMgYSBzdHJvbmcgaW5mbHVlbmNlIG9uIHByaWNlLiBJIGludGVycHJldCB0aGUgbGV0dGVyLXZhbHVlIHBsb3QgYXMgYSBtb3JlIGRldGFpbGVkIHZlcnNpb24gb2YgYSBib3hwbG90IHRoYXQgYmV0dGVyIHJlcHJlc2VudHMgdGhlIHNoYXBlIGFuZCBzcHJlYWQgb2YgbGFyZ2UgZGF0YXNldHMuCmBgCgojIyMgNS4gQ29tcGFyaW5nIFZpc3VhbGl6YXRpb24gTWV0aG9kcwoKYGBge3J9CnA1X3Zpb2xpbiA8LSBnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV92aW9saW4oKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpYW1vbmQgUHJpY2UgYnkgQ3V0IChWaW9saW4gUGxvdCkiLAogICAgeCA9ICJDdXQiLAogICAgeSA9ICJQcmljZSIKICApCgpwNV92aW9saW4KYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMS01YS5wbmciLCBwbG90ID0gcDVfdmlvbGluLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKYGBge3J9CnA1X2hpc3QgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IHByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAwKSArCiAgZmFjZXRfd3JhcCh+Y3V0KSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpYW1vbmQgUHJpY2UgYnkgQ3V0IChGYWNldGVkIEhpc3RvZ3JhbSkiLAogICAgeCA9ICJQcmljZSIsCiAgICB5ID0gIkNvdW50IgogICkKCnA1X2hpc3QKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMS01Yi5wbmciLCBwbG90ID0gcDVfaGlzdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpwNV9mcmVxIDwtIGdncGxvdChkaWFtb25kcywgYWVzKHggPSBwcmljZSwgY29sb3IgPSBjdXQpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDUwMCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaWFtb25kIFByaWNlIGJ5IEN1dCAoRnJlcXVlbmN5IFBvbHlnb24pIiwKICAgIHggPSAiUHJpY2UiLAogICAgeSA9ICJDb3VudCIsCiAgICBjb2xvciA9ICJDdXQiCiAgKQoKcDVfZnJlcQpgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0xLTVjLnBuZyIsIHBsb3QgPSBwNV9mcmVxLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKKipBbnN3ZXI6KioKYGdlb21fdmlvbGluKClgIGlzIHVzZWZ1bCBiZWNhdXNlIGl0IHNob3dzIHRoZSBvdmVyYWxsIHNoYXBlIGFuZCBkZW5zaXR5IG9mIHRoZSBkaXN0cmlidXRpb24gZm9yIGVhY2ggZ3JvdXAgaW4gYSBjb21wYWN0IHdheS4gSXQgaXMgZXNwZWNpYWxseSBoZWxwZnVsIGZvciBjb21wYXJpbmcgaG93IHNwcmVhZCBvdXQgdmFsdWVzIGFyZSBhY3Jvc3MgY2F0ZWdvcmllcy4gSG93ZXZlciwgYmVjYXVzZSBpdCB1c2VzIHNtb290aGluZywgaXQgbWF5IGhpZGUgc29tZSBvZiB0aGUgZmluZXIgZGV0YWlscyBvZiB0aGUgcmF3IGRpc3RyaWJ1dGlvbi4KCkEgZmFjZXRlZCBgZ2VvbV9oaXN0b2dyYW0oKWAgaXMgZWFzaWVyIHRvIGludGVycHJldCBiZWNhdXNlIGl0IHNob3dzIHRoZSBhY3R1YWwgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcyB3aXRoaW4gZWFjaCBjYXRlZ29yeS4gSXQgbWFrZXMgaXQgZWFzaWVyIHRvIHNlZSBwZWFrcywgc2tld25lc3MsIGFuZCBnYXBzIGluIHRoZSBkYXRhLiBUaGUgZG93bnNpZGUgaXMgdGhhdCBpdCB0YWtlcyB1cCBtb3JlIHNwYWNlIGFuZCBjYW4gYmUgaGFyZGVyIHRvIGNvbXBhcmUgZ3JvdXBzIGRpcmVjdGx5IGFjcm9zcyBzZXBhcmF0ZSBwYW5lbHMuCgpBIGNvbG9yZWQgYGdlb21fZnJlcXBvbHkoKWAgaXMgZ29vZCBmb3IgY29tcGFyaW5nIG11bHRpcGxlIGdyb3VwcyBvbiB0aGUgc2FtZSBheGVzLiBJdCBhbGxvd3MgZGlyZWN0IGNvbXBhcmlzb24gb2YgdHJlbmRzIGFjcm9zcyBjYXRlZ29yaWVzIHdpdGhvdXQgdXNpbmcgc2VwYXJhdGUgcGFuZWxzLiBIb3dldmVyLCB0aGUgbGluZXMgY2FuIG92ZXJsYXAgYW5kIGJlY29tZSBjbHV0dGVyZWQsIGVzcGVjaWFsbHkgd2hlbiB0aGVyZSBhcmUgbWFueSBncm91cHMgb3IgbGFyZ2UgZGlmZmVyZW5jZXMgaW4gY291bnRzLgoKVmlvbGluIHBsb3RzIGFyZSBjb21wYWN0IGFuZCBzaG93IHNoYXBlIHdlbGwsIGZhY2V0ZWQgaGlzdG9ncmFtcyBhcmUgdGhlIG1vc3QgZGV0YWlsZWQgYW5kIGVhc2llc3QgdG8gaW50ZXJwcmV0LCBhbmQgZnJlcXVlbmN5IHBvbHlnb25zIGFyZSB1c2VmdWwgZm9yIGNvbXBhcmlzb24gYnV0IGNhbiBiZWNvbWUgdmlzdWFsbHkgbWVzc3kuCgojIyMgNi4gSml0dGVyIEFsdGVybmF0aXZlcyB3aXRoIGdnYmVlc3dhcm0KCmBgYHtyfQpsaWJyYXJ5KGdnYmVlc3dhcm0pCmBgYAoKYGBge3J9CgpwNiA8LSBnZ3Bsb3QobXBnLCBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkgKwogIGdlb21fYmVlc3dhcm0oKSArCiAgbGFicygKICAgIHRpdGxlID0gIkhpZ2h3YXkgTVBHIGJ5IFZlaGljbGUgQ2xhc3MgKEJlZXN3YXJtIFBsb3QpIiwKICAgIHggPSAiQ2xhc3MiLAogICAgeSA9ICJIaWdod2F5IE1QRyIKICApCgpwNgpgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0xLTYucG5nIiwgcGxvdCA9IHA2LCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKCioqQW5zd2VyOioqICAKVGhlIGBnZ2JlZXN3YXJtYCBwYWNrYWdlIHByb3ZpZGVzIHNldmVyYWwgbWV0aG9kcyBzaW1pbGFyIHRvIGBnZW9tX2ppdHRlcigpYCB0aGF0IGhlbHAgcmVkdWNlIG92ZXJwbG90dGluZyB3aGlsZSBzdGlsbCBzaG93aW5nIGluZGl2aWR1YWwgb2JzZXJ2YXRpb25zLgoKLSBgZ2VvbV9iZWVzd2FybSgpYCBhcnJhbmdlcyBwb2ludHMgdG8gYXZvaWQgb3ZlcmxhcCBieSBzcHJlYWRpbmcgdGhlbSBvdXQgaW4gYSBjb250cm9sbGVkIHdheSwgbWFraW5nIHRoZSBkaXN0cmlidXRpb24gZWFzaWVyIHRvIHNlZS4KLSBgZ2VvbV9xdWFzaXJhbmRvbSgpYCBhZGRzIHNtYWxsIG9mZnNldHMgdG8gcG9pbnRzIHVzaW5nIGEgcXVhc2lyYW5kb20gYXBwcm9hY2gsIHNvIHRoZSBwb2ludHMgYXJlIHNwcmVhZCBvdXQgYnV0IHN0aWxsIHJlZmxlY3QgdGhlIHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb24uCi0gYGdlb21fc2luYSgpYCBzcHJlYWRzIHBvaW50cyBiYXNlZCBvbiBsb2NhbCBkZW5zaXR5LCBzbyB3aWRlciBzZWN0aW9ucyBvZiB0aGUgcGxvdCByZXByZXNlbnQgYXJlYXMgd2l0aCBtb3JlIG9ic2VydmF0aW9ucy4KCkNvbXBhcmVkIHdpdGggYGdlb21faml0dGVyKClgLCB0aGVzZSBtZXRob2RzIGRvIGEgYmV0dGVyIGpvYiBvZiBwcmVzZXJ2aW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgd2hpbGUgc3RpbGwgcHJldmVudGluZyBwb2ludHMgZnJvbSBvdmVybGFwcGluZy4KCiMjIEV4ZXJjaXNlcyA3LjUuMi4xCgojIyMgMS4gUmVzY2FsaW5nIENvdW50cyB0byBQcm9wb3J0aW9ucwoKYGBge3J9CgpjdXRfY29sb3IgPC0gZGlhbW9uZHMgJT4lCiAgY291bnQoY29sb3IsIGN1dCkKCmN1dF9jb2xvcgpgYGAKCmBgYHtyfQpjdXRfd2l0aGluX2NvbG9yIDwtIGN1dF9jb2xvciAlPiUKICBncm91cF9ieShjb2xvcikgJT4lCiAgbXV0YXRlKHByb3AgPSBuIC8gc3VtKG4pKQoKY3V0X3dpdGhpbl9jb2xvcgpgYGAKCmBgYHtyfQpwNzIxXzFhIDwtIGdncGxvdChjdXRfd2l0aGluX2NvbG9yLCBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0LCBmaWxsID0gcHJvcCkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDdXQgV2l0aGluIENvbG9yIiwKICAgIHggPSAiQ29sb3IiLAogICAgeSA9ICJDdXQiLAogICAgZmlsbCA9ICJQcm9wb3J0aW9uIgogICkKCnA3MjFfMWEKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMi0xLTFhLnBuZyIsIHBsb3QgPSBwNzIxXzFhLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKYGBge3J9CmNvbG9yX3dpdGhpbl9jdXQgPC0gY3V0X2NvbG9yICU+JQogIGdyb3VwX2J5KGN1dCkgJT4lCiAgbXV0YXRlKHByb3AgPSBuIC8gc3VtKG4pKQoKY29sb3Jfd2l0aGluX2N1dApgYGAKCmBgYHtyfQpwNzIxXzFiIDwtIGdncGxvdChjb2xvcl93aXRoaW5fY3V0LCBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0LCBmaWxsID0gcHJvcCkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb2xvciBXaXRoaW4gQ3V0IiwKICAgIHggPSAiQ29sb3IiLAogICAgeSA9ICJDdXQiLAogICAgZmlsbCA9ICJQcm9wb3J0aW9uIgogICkKCnA3MjFfMWIKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMi0xLTFiLnBuZyIsIHBsb3QgPSBwNzIxXzFiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKKipBbnN3ZXI6KiogIApUaGUgY291bnQgZGF0YXNldCBjYW4gYmUgcmVzY2FsZWQgYnkgY29udmVydGluZyByYXcgY291bnRzIGludG8gcHJvcG9ydGlvbnMgd2l0aGluIGVhY2ggZ3JvdXAuIFRvIHNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjdXQgd2l0aGluIGNvbG91ciwgZWFjaCBjb3VudCBzaG91bGQgYmUgZGl2aWRlZCBieSB0aGUgdG90YWwgY291bnQgZm9yIHRoYXQgY29sb3VyLiBUbyBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgY29sb3VyIHdpdGhpbiBjdXQsIGVhY2ggY291bnQgc2hvdWxkIGJlIGRpdmlkZWQgYnkgdGhlIHRvdGFsIGNvdW50IGZvciB0aGF0IGN1dC4gVGhpcyBtYWtlcyB0aGUgcGF0dGVybnMgZWFzaWVyIHRvIGNvbXBhcmUgYmVjYXVzZSB0aGUgdmFsdWVzIGFyZSBzdGFuZGFyZGl6ZWQgaW5zdGVhZCBvZiBiZWluZyBkcml2ZW4gYnkgZ3JvdXAgc2l6ZS4KCiMjIyAyLiBBdmVyYWdlIEZsaWdodCBEZWxheXMgYnkgRGV0aW5hdGlvbiBhbmQgTW9udGgKCmBgYHtyfQpkZWxheV9ieV9kZXN0X21vbnRoIDwtIGZsaWdodHMgJT4lCiAgbXV0YXRlKHRvdGFsX2RlbGF5ID0gZGVwX2RlbGF5ICsgYXJyX2RlbGF5KSAlPiUKICBmaWx0ZXIoIWlzLm5hKHRvdGFsX2RlbGF5KSkgJT4lCiAgZ3JvdXBfYnkobW9udGgsIGRlc3QpICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKHRvdGFsX2RlbGF5KSwgLmdyb3VwcyA9ICJkcm9wIikKCmRlbGF5X2J5X2Rlc3RfbW9udGgKYGBgCgpgYGB7cn0KcDcyMV8yIDwtIGdncGxvdChkZWxheV9ieV9kZXN0X21vbnRoLCBhZXMoeCA9IG1vbnRoLCB5ID0gZGVzdCwgZmlsbCA9IGF2Z19kZWxheSkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkF2ZXJhZ2UgRmxpZ2h0IERlbGF5IGJ5IERlc3RpbmF0aW9uIGFuZCBNb250aCIsCiAgICB4ID0gIk1vbnRoIiwKICAgIHkgPSAiRGVzdGluYXRpb24iLAogICAgZmlsbCA9ICJBdmVyYWdlIERlbGF5IgogICkKCnA3MjFfMgpgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0yLTEtMi5wbmciLCBwbG90ID0gcDcyMV8yLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCmBgYAoKYGBge3J9CmRlbGF5X2J5X2Rlc3RfbW9udGgyIDwtIGRlbGF5X2J5X2Rlc3RfbW9udGggJT4lCiAgZ3JvdXBfYnkoZGVzdCkgJT4lCiAgbXV0YXRlKG92ZXJhbGxfYXZnID0gbWVhbihhdmdfZGVsYXkpKSAlPiUKICB1bmdyb3VwKCkKCnA3MjFfMl9pbXByb3ZlZCA8LSBnZ3Bsb3QoCiAgZGVsYXlfYnlfZGVzdF9tb250aDIsCiAgYWVzKHggPSBtb250aCwgeSA9IHJlb3JkZXIoZGVzdCwgb3ZlcmFsbF9hdmcpLCBmaWxsID0gYXZnX2RlbGF5KQopICsKICBnZW9tX3RpbGUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkF2ZXJhZ2UgRmxpZ2h0IERlbGF5IGJ5IERlc3RpbmF0aW9uIGFuZCBNb250aCAoUmVvcmRlcmVkKSIsCiAgICB4ID0gIk1vbnRoIiwKICAgIHkgPSAiRGVzdGluYXRpb24iLAogICAgZmlsbCA9ICJBdmVyYWdlIERlbGF5IgogICkKCnA3MjFfMl9pbXByb3ZlZApgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0yLTEtMi1pbXByb3ZlZC5wbmciLCBwbG90ID0gcDcyMV8yX2ltcHJvdmVkLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCmBgYAoKKipBbnN3ZXI6KiogIApUaGUgdGlsZSBwbG90IHNob3dzIGhvdyBhdmVyYWdlIGZsaWdodCBkZWxheXMgdmFyeSBieSBkZXN0aW5hdGlvbiBhbmQgbW9udGgsIGJ1dCBpdCBpcyBkaWZmaWN1bHQgdG8gcmVhZCBiZWNhdXNlIHRoZXJlIGFyZSBtYW55IGRlc3RpbmF0aW9ucyBhbmQgdGhlIHktYXhpcyBiZWNvbWVzIGNyb3dkZWQuIFRoZSBsYXJnZSBudW1iZXIgb2YgZGVzdGluYXRpb24gbGFiZWxzIG1ha2VzIGl0IGhhcmQgdG8gY29tcGFyZSBwYXR0ZXJucyBjbGVhcmx5LiBUaGUgcGxvdCBjYW4gYmUgaW1wcm92ZWQgYnkgcmVvcmRlcmluZyBkZXN0aW5hdGlvbnMsIGZpbHRlcmluZyB0byBvbmx5IGEgc3Vic2V0IG9mIGRlc3RpbmF0aW9ucywgb3IgdXNpbmcgZmFjZXRpbmcgb3IgYSBkaWZmZXJlbnQgdmlzdWFsaXphdGlvbiB0aGF0IHJlZHVjZXMgY2x1dHRlci4gQSByZW9yZGVyZWQgaGVhdG1hcCBtYWtlcyB0aGUgb3ZlcmFsbCBwYXR0ZXJuIGVhc2llciB0byBpbnRlcnByZXQuCgojIyMgMy4gQXhpcyBDaG9pY2UgaW4gVGlsZSBQbG90cwoKKipBbnN3ZXI6KiogIApJdCBpcyBzbGlnaHRseSBiZXR0ZXIgdG8gdXNlIGBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0KWAgYmVjYXVzZSBgY29sb3JgIGhhcyBtb3JlIGNhdGVnb3JpZXMgdGhhbiBgY3V0YCwgYW5kIHBsYWNpbmcgdGhlIHZhcmlhYmxlIHdpdGggbW9yZSBsZXZlbHMgb24gdGhlIHgtYXhpcyBnZW5lcmFsbHkgbWFrZXMgdGhlIHBsb3QgZWFzaWVyIHRvIHJlYWQuIFRoZSB4LWF4aXMgbGFiZWxzIGFyZSBvZnRlbiBlYXNpZXIgdG8gc2Nhbiwgd2hpbGUgdGhlIHktYXhpcyB3b3JrcyB3ZWxsIGZvciB0aGUgdmFyaWFibGUgd2l0aCBmZXdlciBsZXZlbHMuIFNpbmNlIGBjdXRgIGhhcyBmZXdlciBjYXRlZ29yaWVzLCBwdXR0aW5nIGl0IG9uIHRoZSB5LWF4aXMgY3JlYXRlcyBhIGNsZWFuZXIgYW5kIG1vcmUgYmFsYW5jZWQgZGlzcGxheQoKIyMgRXhlcmNpc2VzIDcuNS4zLjEKCiMjIyAxLiBjdXRfd2lkdGggdnMgY3V0X251bWJlcgoKYGBge3J9CmRpYW1vbmRzX3dpZHRoIDwtIGRpYW1vbmRzICU+JQogIG11dGF0ZShwcmljZV9ncm91cCA9IGN1dF93aWR0aChwcmljZSwgd2lkdGggPSA1MDAwKSkKCnA3MzFfMWEgPC0gZ2dwbG90KGRpYW1vbmRzX3dpZHRoLCBhZXMoeCA9IGNhcmF0LCBjb2xvciA9IHByaWNlX2dyb3VwKSkgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiQ2FyYXQgRGlzdHJpYnV0aW9uIGJ5IFByaWNlIEdyb3VwIChjdXRfd2lkdGgpIiwKICAgIHggPSAiQ2FyYXQiLAogICAgeSA9ICJDb3VudCIsCiAgICBjb2xvciA9ICJQcmljZSBHcm91cCIKICApCgpwNzMxXzFhCmBgYAoKYGBge3J9Cmdnc2F2ZSgiZXhlcmNpc2UtNy01LTMtMS0xYS5wbmciLCBwbG90ID0gcDczMV8xYSwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpkaWFtb25kc19udW1iZXIgPC0gZGlhbW9uZHMgJT4lCiAgbXV0YXRlKHByaWNlX2dyb3VwID0gY3V0X251bWJlcihwcmljZSwgbiA9IDYpKQoKcDczMV8xYiA8LSBnZ3Bsb3QoZGlhbW9uZHNfbnVtYmVyLCBhZXMoeCA9IGNhcmF0LCBjb2xvciA9IHByaWNlX2dyb3VwKSkgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiQ2FyYXQgRGlzdHJpYnV0aW9uIGJ5IFByaWNlIEdyb3VwIChjdXRfbnVtYmVyKSIsCiAgICB4ID0gIkNhcmF0IiwKICAgIHkgPSAiQ291bnQiLAogICAgY29sb3IgPSAiUHJpY2UgR3JvdXAiCiAgKQoKcDczMV8xYgpgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0zLTEtMWIucG5nIiwgcGxvdCA9IHA3MzFfMWIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgoqKkFuc3dlcjoqKiAgCldoZW4gdXNpbmcgYSBmcmVxdWVuY3kgcG9seWdvbiB0byBzdW1tYXJpemUgdGhlIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiwgaXQgaXMgaW1wb3J0YW50IHRvIHRoaW5rIGFib3V0IGhvdyB0aGUgY29udGludW91cyB2YXJpYWJsZSBpcyBiZWluZyBncm91cGVkLiBgY3V0X3dpZHRoKClgIGNyZWF0ZXMgYmlucyB3aXRoIGVxdWFsIG51bWVyaWMgd2lkdGgsIHdoaWxlIGBjdXRfbnVtYmVyKClgIGNyZWF0ZXMgYmlucyB3aXRoIHJvdWdobHkgZXF1YWwgbnVtYmVycyBvZiBvYnNlcnZhdGlvbnMuIAoKV2l0aCBgY3V0X3dpZHRoKClgLCB0aGUgZ3JvdXBzIGFyZSBlYXNpZXIgdG8gaW50ZXJwcmV0IGJlY2F1c2UgZWFjaCBiaW4gcmVwcmVzZW50cyB0aGUgc2FtZSByYW5nZSBvZiBwcmljZSB2YWx1ZXMsIGJ1dCBzb21lIGdyb3VwcyBtYXkgY29udGFpbiB2ZXJ5IGRpZmZlcmVudCBudW1iZXJzIG9mIGRpYW1vbmRzLiBXaXRoIGBjdXRfbnVtYmVyKClgLCB0aGUgZ3JvdXAgc2l6ZXMgYXJlIG1vcmUgYmFsYW5jZWQsIHdoaWNoIGNhbiBtYWtlIHRoZSBjb21wYXJpc29uIG9mIGRpc3RyaWJ1dGlvbnMgZWFzaWVyLCBidXQgdGhlIHByaWNlIHJhbmdlcyBhcmUgbm8gbG9uZ2VyIGVxdWFsbHkgc3BhY2VkLiAKClRoaXMgYWZmZWN0cyBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIDJkIGRpc3RyaWJ1dGlvbiBvZiBgY2FyYXRgIGFuZCBgcHJpY2VgIGJlY2F1c2UgdGhlIGFwcGFyZW50IHBhdHRlcm5zIGNhbiBjaGFuZ2UgZGVwZW5kaW5nIG9uIGhvdyB0aGUgZ3JvdXBzIGFyZSBmb3JtZWQuIEVxdWFsLXdpZHRoIGJpbnMgZW1waGFzaXplIHRoZSBhY3R1YWwgc2NhbGUgb2YgcHJpY2UsIHdoaWxlIGVxdWFsLWNvdW50IGJpbnMgZW1waGFzaXplIGNvbXBhcmFiaWxpdHkgYmV0d2VlbiBncm91cHMuCgojIyMgMi4gQ2FyYXQgRGlzdHJpYnV0aW9uIGJ5IFByaWNlCgpgYGB7cn0KZGlhbW9uZHNfcHJpY2VfZ3JvdXBzIDwtIGRpYW1vbmRzICU+JQogIG11dGF0ZShwcmljZV9ncm91cCA9IGN1dF9udW1iZXIocHJpY2UsIG4gPSA2KSkKCnA3MzFfMiA8LSBnZ3Bsb3QoZGlhbW9uZHNfcHJpY2VfZ3JvdXBzLCBhZXMoeCA9IGNhcmF0LCBjb2xvciA9IHByaWNlX2dyb3VwKSkgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIENhcmF0IFBhcnRpdGlvbmVkIGJ5IFByaWNlIiwKICAgIHggPSAiQ2FyYXQiLAogICAgeSA9ICJDb3VudCIsCiAgICBjb2xvciA9ICJQcmljZSBHcm91cCIKICApCgpwNzMxXzIKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMy0xLTIucG5nIiwgcGxvdCA9IHA3MzFfMiwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCioqQW5zd2VyOioqICAKVGhlIGZyZXF1ZW5jeSBwb2x5Z29uIHNob3dzIHRoYXQgaGlnaGVyIHByaWNlIGdyb3VwcyB0ZW5kIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBsYXJnZXIgY2FyYXQgdmFsdWVzLiBMb3dlciBwcmljZSBncm91cHMgYXJlIGNvbmNlbnRyYXRlZCBhdCBzbWFsbGVyIGNhcmF0IHNpemVzLCB3aGlsZSBoaWdoZXIgcHJpY2UgZ3JvdXBzIHNoaWZ0IHRvIHRoZSByaWdodC4gVGhpcyBzdWdnZXN0cyBhIHN0cm9uZyBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjYXJhdCBhbmQgcHJpY2UuCgojIyMgMy4gUHJpY2UgQ29tcGFyaXNvbiBieSBEaWFtb25kIFNpemUKCmBgYHtyfQoKZGlhbW9uZHNfc2l6ZV9jb21wYXJlIDwtIGRpYW1vbmRzICU+JQogIG11dGF0ZShzaXplX2dyb3VwID0gY2FzZV93aGVuKAogICAgY2FyYXQgPCAxIH4gIlNtYWxsICg8MSBjYXJhdCkiLAogICAgY2FyYXQgPj0gMi41IH4gIlZlcnkgbGFyZ2UgKD49Mi41IGNhcmF0cykiLAogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8KICApKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHNpemVfZ3JvdXApKQoKcDczMV8zIDwtIGdncGxvdChkaWFtb25kc19zaXplX2NvbXBhcmUsIGFlcyh4ID0gcHJpY2UsIHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpLCBjb2xvciA9IHNpemVfZ3JvdXApKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDUwMCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQcmljZSBEaXN0cmlidXRpb246IFNtYWxsIHZzIFZlcnkgTGFyZ2UgRGlhbW9uZHMiLAogICAgeCA9ICJQcmljZSIsCiAgICB5ID0gIkRlbnNpdHkiLAogICAgY29sb3IgPSAiU2l6ZSBHcm91cCIKICApCgpwNzMxXzMKYGBgCgpgYGB7cn0KZ2dzYXZlKCJleGVyY2lzZS03LTUtMy0xLTMucG5nIiwgcGxvdCA9IHA3MzFfMywgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQpgYGAKCioqQW5zd2VyOioqClZlcnkgbGFyZ2UgZGlhbW9uZHMgaGF2ZSBhIHByaWNlIGRpc3RyaWJ1dGlvbiB0aGF0IGlzIHNoaWZ0ZWQgbXVjaCBmYXJ0aGVyIHRvIHRoZSByaWdodCB0aGFuIHNtYWxsIGRpYW1vbmRzLCBtZWFuaW5nIHRoZXkgYXJlIGdlbmVyYWxseSBtdWNoIG1vcmUgZXhwZW5zaXZlLiBUaGlzIGlzIHdoYXQgSSB3b3VsZCBleHBlY3QsIHNpbmNlIGxhcmdlciBkaWFtb25kcyBhcmUgcmFyZXIgYW5kIGNhcmF0IGhhcyBhIHN0cm9uZyBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgd2l0aCBwcmljZS4gVXNpbmcgZGVuc2l0eSBpbnN0ZWFkIG9mIGNvdW50IG1ha2VzIHRoZSBjb21wYXJpc29uIGNsZWFyZXIgYmVjYXVzZSBpdCBzaG93cyB0aGUgc2hhcGUgb2YgdGhlIHByaWNlIGRpc3RyaWJ1dGlvbnMgd2l0aG91dCBsZXR0aW5nIHRoZSBtdWNoIGxhcmdlciBudW1iZXIgb2Ygc21hbGwgZGlhbW9uZHMgZG9taW5hdGUgdGhlIGdyYXBoLgoKIyMjIDQuIENvbWJpbmVkIFZpc3VhbGl6YXRpb24gb2YgQ3V0LCBDYXJhdCwgYW5kIFByaWNlCgpgYGB7cn0KZGlhbW9uZHNfY2FyYXRfZ3JvdXBzIDwtIGRpYW1vbmRzICU+JQogIGZpbHRlcihjYXJhdCA+IDAsIGNhcmF0IDw9IDMpICU+JQogIG11dGF0ZShjYXJhdF9ncm91cCA9IGN1dF93aWR0aChjYXJhdCwgd2lkdGggPSAwLjUpKQoKcDczMV80IDwtIGdncGxvdChkaWFtb25kc19jYXJhdF9ncm91cHMsIGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofmNhcmF0X2dyb3VwKSArCiAgbGFicygKICAgIHRpdGxlID0gIlByaWNlIGJ5IEN1dCwgRmFjZXRlZCBieSBDYXJhdCBHcm91cCIsCiAgICB4ID0gIkN1dCIsCiAgICB5ID0gIlByaWNlIgogICkKCnA3MzFfNApgYGAKCmBgYHtyfQpnZ3NhdmUoImV4ZXJjaXNlLTctNS0zLTEtNC5wbmciLCBwbG90ID0gcDczMV80LCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQpgYGAKCioqQW5zd2VyOioqICAKVGhpcyBwbG90IGNvbWJpbmVzIGJveHBsb3RzIGFuZCBmYWNldGluZyB0byBzaG93IHRoZSBqb2ludCBkaXN0cmlidXRpb24gb2YgY3V0LCBjYXJhdCwgYW5kIHByaWNlLiBUaGUgYm94cGxvdHMgY29tcGFyZSBwcmljZSBhY3Jvc3MgY3V0IGNhdGVnb3JpZXMsIHdoaWxlIHRoZSBmYWNldHMgc2VwYXJhdGUgZGlhbW9uZHMgaW50byBjYXJhdCBncm91cHMuIFRoaXMgbWFrZXMgaXQgZWFzaWVyIHRvIHNlZSBob3cgcHJpY2UgdmFyaWVzIGJ5IGN1dCB3aGVuIGRpYW1vbmRzIGFyZSBncm91cGVkIGludG8gc2ltaWxhciBjYXJhdCByYW5nZXMuIFRoZSBwbG90IGFsc28gc2hvd3MgdGhhdCBwcmljZSBnZW5lcmFsbHkgaW5jcmVhc2VzIGFzIGNhcmF0IGluY3JlYXNlcy4gT3ZlcmFsbCwgY2FyYXQgYXBwZWFycyB0byBoYXZlIGEgc3Ryb25nIGVmZmVjdCBvbiBwcmljZSwgYW5kIHRoZSBpbmZsdWVuY2Ugb2YgY3V0IGJlY29tZXMgZWFzaWVyIHRvIGludGVycHJldCB3aGVuIGRpYW1vbmRzIGFyZSBjb21wYXJlZCB3aXRoaW4gc2ltaWxhciBjYXJhdCByYW5nZXMuCgojIyMgNS4gV2h5IFNjYXR0ZXJwbG90cyBSZXZlYWwgMkQgT3V0bGllcnMKCmBgYHtyfQpwNzMxXzUgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IHgsIHkgPSB5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDQsIDExKSwgeWxpbSA9IGMoNCwgMTEpKSArCiAgbGFicygKICAgIHRpdGxlID0gIlNjYXR0ZXJwbG90IG9mIERpYW1vbmQgeCBhbmQgeSBEaW1lbnNpb25zIiwKICAgIHggPSAieCIsCiAgICB5ID0gInkiCiAgKQoKcDczMV81CmBgYAoKYGBge3J9Cmdnc2F2ZSgiZXhlcmNpc2UtNy01LTMtMS01LnBuZyIsIHBsb3QgPSBwNzMxXzUsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgoqKkFuc3dlcjoqKgpBIHNjYXR0ZXJwbG90IGlzIGEgYmV0dGVyIGRpc3BsYXkgaW4gdGhpcyBjYXNlIGJlY2F1c2UgaXQgc2hvd3MgZWFjaCBvYnNlcnZhdGlvbiBpbmRpdmlkdWFsbHkgYW5kIG1ha2VzIHVudXN1YWwgY29tYmluYXRpb25zIG9mIGB4YCBhbmQgYHlgIGVhc2llciB0byBkZXRlY3QuIFNvbWUgZGlhbW9uZHMgaGF2ZSBgeGAgYW5kIGB5YCB2YWx1ZXMgdGhhdCBsb29rIG5vcm1hbCB3aGVuIGV4YW1pbmVkIHNlcGFyYXRlbHksIGJ1dCB0b2dldGhlciB0aGV5IGRvIG5vdCBmb2xsb3cgdGhlIG1haW4gcGF0dGVybiBvZiB0aGUgZGF0YS4gQSBiaW5uZWQgcGxvdCB3b3VsZCBncm91cCBuZWFyYnkgb2JzZXJ2YXRpb25zIGludG8gdGlsZXMgb3IgYmlucywgd2hpY2ggY291bGQgc21vb3RoIG92ZXIgdGhvc2UgdW51c3VhbCBwb2ludHMgYW5kIG1ha2UgdGhlbSBoYXJkZXIgdG8gaWRlbnRpZnkuIEJlY2F1c2UgdGhlIGdvYWwgaXMgdG8gZmluZCBvdXRsaWVycyBpbiB0aGUgdHdvLWRpbWVuc2lvbmFsIHJlbGF0aW9uc2hpcCwgdGhlIHNjYXR0ZXJwbG90IGlzIG1vcmUgaW5mb3JtYXRpdmUuCmBgCi4uLgoK