diff --git a/analyses/hello-clusters/02_compare-clustering-parameters.Rmd b/analyses/hello-clusters/02_compare-clustering-parameters.Rmd
index e5ffb6c5c..6ba7df341 100644
--- a/analyses/hello-clusters/02_compare-clustering-parameters.Rmd
+++ b/analyses/hello-clusters/02_compare-clustering-parameters.Rmd
@@ -361,7 +361,9 @@ umap_plots <- cluster_results_list |>
theme(
axis.ticks = element_blank(),
axis.text = element_blank(),
- legend.position = "bottom"
+ # Ensure legends fit in the figure
+ legend.position = "bottom",
+ legend.key.size = unit(0.2, "cm")
)
}
)
diff --git a/analyses/hello-clusters/02_compare-clustering-parameters.nb.html b/analyses/hello-clusters/02_compare-clustering-parameters.nb.html
index 212e116c5..76257489e 100644
--- a/analyses/hello-clusters/02_compare-clustering-parameters.nb.html
+++ b/analyses/hello-clusters/02_compare-clustering-parameters.nb.html
@@ -11,7 +11,7 @@
-
+
Comparing clustering parameters with rOpenScPCA
@@ -2901,7 +2901,7 @@
Comparing clustering parameters with
rOpenScPCA
Data Lab
-2025-01-13
+2025-01-14
@@ -3040,7 +3040,7 @@ Varying a single clustering parameter
listed below. Clusters will be calculated for all combinations of
parameters values (where applicable); default values that the function
will use for any unspecified parameter values are shown in
-parentheses
+parentheses.
algorithm
: Which clustering algorithm to use
(Louvain)
@@ -3051,8 +3051,8 @@ Varying a single clustering parameter
objective_function
: The objective function to optimize
clusters (CPM; used only with Leiden clustering)
-rOpenScPCA::sweep_clusters()
does allow you to specify
-values for any other parameters.
+rOpenScPCA::sweep_clusters()
does not allow you to
+specify values for any other parameters.
This function will return a list of data frames of clustering
results. Each data frame will have the following columns:
@@ -3066,9 +3066,9 @@ Varying a single clustering parameter
Louvain algorithm while varying the nn
parameter:
-
+
# Define nn parameter values of interest
-nn_values <- seq(10, 30, 10)
+nn_values <- c(10, 20, 30)
# Calculate clusters varying nn, but leaving other parameters at their default values
cluster_results_list <- rOpenScPCA::sweep_clusters(
@@ -3091,7 +3091,8 @@ Varying a single clustering parameter
It can be helpful (although it is not strictly necessary to keep
-track) to name this list by the varied nn
parameter:
+track) to name this list by the varied nn
parameter. In
+this case, we’ll use these names to label plots.
@@ -3173,7 +3174,7 @@ Visualizing clustering results
the UMAPs.
-
+
umap_plots <- cluster_results_list |>
purrr::imap(
\(cluster_df, clustering_name) {
@@ -3196,7 +3197,7 @@ Visualizing clustering results
)
# Print the plots with patchwork::wrap_plots()
-patchwork::wrap_plots(umap_plots)
+patchwork::wrap_plots(umap_plots, ncol = 3)
@@ -3221,31 +3222,19 @@ Silhouette width and neighborhood purity
cluster_results_list
to calculate these quantities.
-
+
cell_metric_list <- cluster_results_list |>
purrr::map(
\(cluster_df) {
# calculate silhouette width
silhouette_df <- rOpenScPCA::calculate_silhouette(pca_matrix, cluster_df)
- # calculate neighbhorhood purity
- purity_df <- rOpenScPCA::calculate_purity(pca_matrix, cluster_df)
-
- # Combine into a single data frame
- dplyr::left_join(silhouette_df, purity_df)
+ # calculate neighbhorhood purity, starting from silhouette_df
+ rOpenScPCA::calculate_purity(pca_matrix, silhouette_df)
}
- )
-
-
-Joining with `by = join_by(cell_id, cluster, algorithm, weighting, nn,
-resolution)`
-Joining with `by = join_by(cell_id, cluster, algorithm, weighting, nn,
-resolution)`
-Joining with `by = join_by(cell_id, cluster, algorithm, weighting, nn,
-resolution)`
-
-
-# View the first six rows of each clustering result's cell-level QC metrics
+ )
+
+# View the first six rows of each clustering result's cell-level QC metrics
purrr::map(cell_metric_list, head)
@@ -3304,8 +3293,8 @@ Silhouette width and neighborhood purity
nn
column will distinguish among conditions.
-
-cell_metrics_df <- dplyr::bind_rows(cell_metric_list)
+
+cell_metrics_df <- purrr::list_rbind(cell_metric_list)
@@ -3347,7 +3336,10 @@ Silhouette width and neighborhood purity
While there does not appear to be a salient difference among
silhouette width distributions, it does appear that purity is higher
-with a higher nearest neighbors parameter.
+with a higher nearest neighbors parameter. It’s worth noting that this
+trend in purity values is expected: Higher nearest neighbor parameter
+values lead to fewer clusters, and neighborhood purity tends to be
+higher when there are fewer clusters.
Stability
@@ -3357,11 +3349,11 @@
Stability
iteration.
-
+
stability_list <- cluster_results_list |>
purrr::map(
\(cluster_df) {
- nn <- unique(cluster_df$nn)
+ nn <- cluster_df$nn[1] # all rows have the same `nn` parameter, so we'll take the first
# calculate stability, passing in the parameter value used for this iteration
rOpenScPCA::calculate_stability(pca_matrix, cluster_df, nn = nn)
@@ -3375,8 +3367,8 @@ Stability
nn
parameterizations.
-
-stability_df <- dplyr::bind_rows(stability_list)
+
+stability_df <- purrr::list_rbind(stability_list)
ggplot(stability_df) +
aes(x = as.factor(nn), y = ari, fill = as.factor(nn)) +
@@ -3408,10 +3400,10 @@ Varying multiple clustering parameters
examples) and visualize results.
-
+
# Define vectors of parameters to vary
-nn_values <- seq(10, 30, 10)
-res_values <- seq(5, 15, 5) / 10
+nn_values <- c(10, 20, 30)
+res_values <- c(0.5, 1.0, 1.5)
cluster_results_list <- rOpenScPCA::sweep_clusters(
@@ -3442,7 +3434,7 @@ Visualize clusters
UMAP panel title.
-
+
umap_plots <- cluster_results_list |>
purrr::map(
\(cluster_df) {
@@ -3452,7 +3444,7 @@ Visualize clusters
# Create a title for the UMAP with both parameters
umap_title <- glue::glue(
- "nn: {unique(cluster_df$nn)}; res: {unique(cluster_df$resolution)}"
+ "nn: {cluster_df$nn[1]}; res: {cluster_df$resolution[1]}"
)
# Plot the UMAP, colored by the new cluster variable
@@ -3464,16 +3456,18 @@ Visualize clusters
theme(
axis.ticks = element_blank(),
axis.text = element_blank(),
- legend.position = "bottom"
+ # Ensure legends fit in the figure
+ legend.position = "bottom",
+ legend.key.size = unit(0.2, "cm")
)
}
)
# Print the plots with patchwork::wrap_plots()
-patchwork::wrap_plots(umap_plots)
+patchwork::wrap_plots(umap_plots, ncol = 3)
-
+
@@ -3492,36 +3486,26 @@ Neighborhood purity
single data frame.
-
+
purity_df <- cluster_results_list |>
purrr::map(
\(cluster_df) {
rOpenScPCA::calculate_purity(pca_matrix, cluster_df)
}
) |>
- dplyr::bind_rows()
+ purrr::list_rbind()
-We’ll add a column resolution_label
which we’ll use to
-have informative panel titles in the faceted ggplot we make next.
-
-purity_df <- purity_df |>
- dplyr::mutate(resolution_label = glue::glue("Resolution: {resolution}"))
-
-
-
-
-
-
+
ggplot(purity_df) +
aes(x = as.factor(nn), y = purity, fill = as.factor(nn)) +
geom_boxplot() +
scale_fill_brewer(palette = "Pastel2") +
- # facet by resolution
- facet_wrap(vars(resolution_label)) +
+ # facet by resolution, labeling panels with both the resolution column name and value
+ facet_wrap(vars(resolution), labeller = label_both) +
labs(
x = "Number of nearest neighbors",
y = "Neighborhood purity"
@@ -3529,7 +3513,7 @@ Neighborhood purity
theme(legend.position = "none")
-
+
@@ -3542,7 +3526,7 @@ Stability
plot interpretation, and finally make our plot.
-
+
stability_df <- cluster_results_list |>
purrr::map(
\(cluster_df) {
@@ -3558,22 +3542,18 @@ Stability
)
}
) |>
- dplyr::bind_rows()
-
-stability_df <- stability_df |>
- dplyr::mutate(resolution_label = glue::glue("Resolution: {resolution}"))
+ purrr::list_rbind()
-
+
ggplot(stability_df) +
aes(x = as.factor(nn), y = ari, fill = as.factor(nn)) +
geom_boxplot() +
scale_fill_brewer(palette = "Pastel2") +
- # facet by resolution
- facet_wrap(vars(resolution_label)) +
+ facet_wrap(vars(resolution), labeller = label_both) +
labs(
x = "Number of nearest neighbors",
y = "Adjusted Rand Index"
@@ -3581,7 +3561,7 @@ Stability
theme(legend.position = "none")
-
+
@@ -3651,7 +3631,7 @@ Session Info
-LS0tCnRpdGxlOiAiQ29tcGFyaW5nIGNsdXN0ZXJpbmcgcGFyYW1ldGVycyB3aXRoIHJPcGVuU2NQQ0EiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYXV0aG9yOiAiRGF0YSBMYWIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkNsdXN0ZXJpbmcgYWxnb3JpdGhtcyBoYXZlIHNldmVyYWwgcGFyYW1ldGVycyB3aGljaCBjYW4gYmUgdmFyaWVkLCBsZWFkaW5nIHRvIGRpZmZlcmVudCBjbHVzdGVyaW5nIHJlc3VsdHMuCkEga2V5IHF1ZXN0aW9uIHdoZW4gY2x1c3RlcmluZywgdGhlcmVmb3JlLCBpcyBob3cgdG8gaWRlbnRpZnkgYSBzZXQgb2YgcGFyYW1ldGVycyB0aGF0IGxlYWQgdG8gcm9idXN0IGFuZCByZWxpYWJsZSBjbHVzdGVycyB0aGF0IGNhbiBiZSB1c2VkIGluIGRvd25zdHJlYW0gYW5hbHlzaXMuIAoKVGhpcyBub3RlYm9vayBwcm92aWRlcyBleGFtcGxlcyBvZiBob3cgdG8gdXNlIHRoZSBgck9wZW5TY1BDQWAgcGFja2FnZSB0bzoKCiogQ2FsY3VsYXRlIHNldmVyYWwgdmVyc2lvbnMgb2YgY2x1c3RlcmluZyByZXN1bHRzIGFjcm9zcyBzZXZlcmFsIGRpZmZlcmVudCBwYXJhbWV0ZXJpemF0aW9ucwoqIENhbGN1bGF0ZSBRQyBtZXRyaWNzIG9uIGFjcm9zcyBjbHVzdGVyaW5nIHJlc3VsdHMKClBsZWFzZSByZWZlciB0byB0aGUgW2AwMV9wZXJmb3JtLWV2YWx1YXRlLWNsdXN0ZXJpbmcuUm1kYF0oMDFfcGVyZm9ybS1ldmFsdWF0ZS1jbHVzdGVyaW5nLlJtZCkgbm90ZWJvb2sgZm9yIGEgdHV0b3JpYWwgb24gdXNpbmcgYHJPcGVuU2NQQ0FgIGZ1bmN0aW9ucyB0bzoKCiogQ2FsY3VsYXRlIGNsdXN0ZXJzIGZyb20gYSBzaW5nbGUgcGFyYW1ldGVyaXphdGlvbgoqIENhbGN1bGF0ZSBRQyBtZXRyaWNzIG9uIGEgc2luZ2xlIHNldCBvZiBjbHVzdGVycywgYXMgd2VsbCBhcyBleHBsYW5hdGlvbnMgb2YgdGhlIG1ldHJpY3MgdGhlbXNlbHZlcwoKVGhpcyBub3RlYm9vayB3aWxsIHVzZSB0aGUgc2FtcGxlIGBTQ1BDUzAwMDAwMWAgZnJvbSBwcm9qZWN0IGBTQ1BDUDAwMDAwMWAsIHdoaWNoIGlzIGFzc3VtZWQgcHJlc2VudCBpbiB0aGUgYE9wZW5TY1BDQS1hbmFseXNpcy9kYXRhL2N1cnJlbnQvU0NQQ1AwMDAwMDFgIGRpcmVjdG9yeSwgZm9yIGFsbCBleGFtcGxlcy4KUGxlYXNlIFtzZWUgdGhpcyBkb2N1bWVudGF0aW9uXShodHRwczovL29wZW5zY3BjYS5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvZ2V0dGluZy1zdGFydGVkL2FjY2Vzc2luZy1yZXNvdXJjZXMvZ2V0dGluZy1hY2Nlc3MtdG8tZGF0YS8pIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IG9idGFpbmluZyBTY1BDQSBkYXRhLgoKIyMgU2V0dXAKCiMjIyBQYWNrYWdlcwoKCmBgYHtyIHBhY2thZ2VzfQpsaWJyYXJ5KHJPcGVuU2NQQ0EpCgpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkocGF0Y2h3b3JrKQp9KQoKIyBTZXQgZ2dwbG90IHRoZW1lIGZvciBwbG90cwp0aGVtZV9zZXQodGhlbWVfYncoKSkKYGBgCgoKIyMjIFBhdGhzCgpgYGB7ciBiYXNlIHBhdGhzfQojIFRoZSBiYXNlIHBhdGggZm9yIHRoZSBPcGVuU2NQQ0EgcmVwb3NpdG9yeQpyZXBvc2l0b3J5X2Jhc2UgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0aHViIikpCgojIFRoZSBjdXJyZW50IGRhdGEgZGlyZWN0b3J5LCBmb3VuZCB3aXRoaW4gdGhlIHJlcG9zaXRvcnkgYmFzZSBkaXJlY3RvcnkKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHJlcG9zaXRvcnlfYmFzZSwgImRhdGEiLCAiY3VycmVudCIpCgojIFRoZSBwYXRoIHRvIHRoaXMgbW9kdWxlCm1vZHVsZV9iYXNlIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJhbmFseXNlcyIsICJoZWxsby1jbHVzdGVycyIpCmBgYAoKYGBge3IgaW5wdXQgZmlsZSBwYXRofQojIFBhdGggdG8gcHJvY2Vzc2VkIFNDRSBmaWxlIGZvciBzYW1wbGUgU0NQQ1MwMDAwMDEKaW5wdXRfc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiU0NQQ1AwMDAwMDEiLCAiU0NQQ1MwMDAwMDEiLCAiU0NQQ0wwMDAwMDFfcHJvY2Vzc2VkLnJkcyIpCmBgYAoKCiMjIyBTZXQgdGhlIHJhbmRvbSBzZWVkCgpCZWNhdXNlIGNsdXN0ZXJpbmcgaW52b2x2ZXMgcmFuZG9tIHNhbXBsaW5nLCBpdCBpcyBpbXBvcnRhbnQgdG8gc2V0IHRoZSByYW5kb20gc2VlZCBhdCB0aGUgdG9wIG9mIHlvdXIgYW5hbHlzaXMgc2NyaXB0IG9yIG5vdGVib29rIHRvIGVuc3VyZSByZXByb2R1Y2liaWxpdHkuCgpgYGB7ciBzZXQgc2VlZH0Kc2V0LnNlZWQoMjAyNCkKYGBgCgojIyBSZWFkIGluIGFuZCBwcmVwYXJlIGRhdGEKClRvIGJlZ2luLCB3ZSdsbCByZWFkIGluIHRoZSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIChTQ0UpIG9iamVjdC4KCmBgYHtyIHJlYWQgZGF0YX0KIyBSZWFkIHRoZSBTQ0UgZmlsZQpzY2UgPC0gcmVhZFJEUyhpbnB1dF9zY2VfZmlsZSkKYGBgCgpGb3IgdGhlIGluaXRpYWwgY2x1c3RlciBjYWxjdWxhdGlvbnMgYW5kIGV2YWx1YXRpb25zLCB3ZSB3aWxsIHVzZSB0aGUgUENBIG1hdHJpeCBleHRyYWN0ZWQgZnJvbSB0aGUgU0NFIG9iamVjdC4KQXMgc2hvd24gaW4gW2AwMV9wZXJmb3JtLWV2YWx1YXRlLWNsdXN0ZXJpbmcuUm1kYF0oMDFfcGVyZm9ybS1ldmFsdWF0ZS1jbHVzdGVyaW5nLlJtZCksIGl0IGlzIGFsc28gcG9zc2libGUgdG8gdXNlIGFuIFNDRSBvYmplY3Qgb3IgYSBTZXVyYXQgb2JqZWN0IGRpcmVjdGx5LgoKCmBgYHtyIGV4dHJhY3QgcGNhIGRhdGF9CiMgRXh0cmFjdCB0aGUgUENBIG1hdHJpeCBmcm9tIGFuIFNDRSBvYmplY3QKcGNhX21hdHJpeCA8LSByZWR1Y2VkRGltKHNjZSwgIlBDQSIpCmBgYAoKIyMgVmFyeWluZyBhIHNpbmdsZSBjbHVzdGVyaW5nIHBhcmFtZXRlcgoKVGhpcyBzZWN0aW9uIHdpbGwgc2hvdyBob3cgdG8gcGVyZm9ybSBjbHVzdGVyaW5nIGFjcm9zcyBhIHNldCBvZiBwYXJhbWV0ZXJzIChha2EsICJzd2VlcCIgYSBzZXQgb2YgcGFyYW1ldGVycykgd2l0aCBgck9wZW5TY1BDQTo6c3dlZXBfY2x1c3RlcnMoKWAuIAoKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIFBDQSBtYXRyaXggd2l0aCByb3cgbmFtZXMgcmVwcmVzZW50aW5nIHVuaXF1ZSBjZWxsIGlkcyAoZS5nLiwgYmFyY29kZXMpIGFzIGl0cyBwcmltYXJ5IGFyZ3VtZW50LCB3aXRoIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBjbHVzdGVyIHBhcmFtZXRlcnMuIApUaGlzIGZ1bmN0aW9uIHdyYXBzIHRoZSBgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX2NsdXN0ZXJzKClgIGZ1bmN0aW9uIGJ1dCBhbGxvd3MgeW91IHRvIHByb3ZpZGUgYSB2ZWN0b3Igb2YgcGFyYW1ldGVyIHZhbHVlcyB0byBwZXJmb3JtIGNsdXN0ZXJpbmcgYWNyb3NzLCBhcyBsaXN0ZWQgYmVsb3cuCkNsdXN0ZXJzIHdpbGwgYmUgY2FsY3VsYXRlZCBmb3IgYWxsIGNvbWJpbmF0aW9ucyBvZiBwYXJhbWV0ZXJzIHZhbHVlcyAod2hlcmUgYXBwbGljYWJsZSk7IGRlZmF1bHQgdmFsdWVzIHRoYXQgdGhlIGZ1bmN0aW9uIHdpbGwgdXNlIGZvciBhbnkgdW5zcGVjaWZpZWQgcGFyYW1ldGVyIHZhbHVlcyBhcmUgc2hvd24gaW4gcGFyZW50aGVzZXMKCiogYGFsZ29yaXRobWA6IFdoaWNoIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHRvIHVzZSAoTG91dmFpbikKKiBgd2VpZ2h0aW5nYDogV2hpY2ggd2VpZ2h0aW5nIHNjaGVtZSB0byB1c2UgKEphY2NhcmQpCiogYG5uYDogVGhlIG51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyAoMTApCiogYHJlc29sdXRpb25gOiBUaGUgcmVzb2x1dGlvbiBwYXJhbWV0ZXIgKDE7IHVzZWQgb25seSB3aXRoIExvdXZhaW4gYW5kIExlaWRlbiBjbHVzdGVyaW5nKQoqIGBvYmplY3RpdmVfZnVuY3Rpb25gOiBUaGUgb2JqZWN0aXZlIGZ1bmN0aW9uIHRvIG9wdGltaXplIGNsdXN0ZXJzIChDUE07IHVzZWQgb25seSB3aXRoIExlaWRlbiBjbHVzdGVyaW5nKQoKYHJPcGVuU2NQQ0E6OnN3ZWVwX2NsdXN0ZXJzKClgIGRvZXMgYWxsb3cgeW91IHRvIHNwZWNpZnkgdmFsdWVzIGZvciBhbnkgb3RoZXIgcGFyYW1ldGVycy4gCgoKVGhpcyBmdW5jdGlvbiB3aWxsIHJldHVybiBhIGxpc3Qgb2YgZGF0YSBmcmFtZXMgb2YgY2x1c3RlcmluZyByZXN1bHRzLiAKRWFjaCBkYXRhIGZyYW1lIHdpbGwgaGF2ZSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6CgoqIGBjZWxsX2lkYDogVW5pcXVlIGNlbGwgaWRlbnRpZmllcnMsIG9idGFpbmVkIGZyb20gdGhlIFBDQSBtYXRyaXgncyByb3cgbmFtZXMKKiBgY2x1c3RlcmA6IEEgZmFjdG9yIGNvbHVtbiB3aXRoIHRoZSBjbHVzdGVyIGlkZW50aXRpZXMKKiBUaGVyZSB3aWxsIGJlIG9uZSBjb2x1bW4gZm9yIGVhY2ggY2x1c3RlcmluZyBwYXJhbWV0ZXIgdXNlZAoKVG8gZGVtb25zdHJhdGUgdGhpcyBmdW5jdGlvbiwgd2UnbGwgY2FsY3VsYXRlIGNsdXN0ZXJzIHVzaW5nIHRoZSBMb3V2YWluIGFsZ29yaXRobSB3aGlsZSB2YXJ5aW5nIHRoZSBgbm5gIHBhcmFtZXRlcjoKCmBgYHtyIHN3ZWVwIGNsdXN0ZXJzfQojIERlZmluZSBubiBwYXJhbWV0ZXIgdmFsdWVzIG9mIGludGVyZXN0Cm5uX3ZhbHVlcyA8LSBzZXEoMTAsIDMwLCAxMCkKCiMgQ2FsY3VsYXRlIGNsdXN0ZXJzIHZhcnlpbmcgbm4sIGJ1dCBsZWF2aW5nIG90aGVyIHBhcmFtZXRlcnMgYXQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMKY2x1c3Rlcl9yZXN1bHRzX2xpc3QgPC0gck9wZW5TY1BDQTo6c3dlZXBfY2x1c3RlcnMoCiAgcGNhX21hdHJpeCwKICBubiA9IG5uX3ZhbHVlcwopCmBgYAoKVGhlIHJlc3VsdGluZyBsaXN0IGhhcyBhIGxlbmd0aCBvZiB0aHJlZSwgb25lIGRhdGEgZnJhbWUgZm9yIGVhY2ggYG5uYCBwYXJhbWV0ZXIgdGVzdGVkOgoKYGBge3IgbGVuZ3RoIGNsdXN0ZXJfcmVzdWx0c19saXN0fQpsZW5ndGgoY2x1c3Rlcl9yZXN1bHRzX2xpc3QpCmBgYAoKSXQgY2FuIGJlIGhlbHBmdWwgKGFsdGhvdWdoIGl0IGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkgdG8ga2VlcCB0cmFjaykgdG8gbmFtZSB0aGlzIGxpc3QgYnkgdGhlIHZhcmllZCBgbm5gIHBhcmFtZXRlcjoKCmBgYHtyIHNldCBsaXN0IG5hbWVzfQpuYW1lcyhjbHVzdGVyX3Jlc3VsdHNfbGlzdCkgPC0gZ2x1ZTo6Z2x1ZSgibm5fe25uX3ZhbHVlc30iKQpgYGAKCgpXZSBjYW4gbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgZWFjaCBkYXRhIGZyYW1lIHVzaW5nIFtgcHVycnI6Om1hcCgpYF0oaHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tYXAuaHRtbCkgdG8gaXRlcmF0ZSBvdmVyIHRoZSBsaXN0OgoKCmBgYHtyIG1hcCBjbHVzdGVyX3Jlc3VsdHNfbGlzdH0KY2x1c3Rlcl9yZXN1bHRzX2xpc3QgfD4KICBwdXJycjo6bWFwKGhlYWQpCmBgYAoKR2VuZXJhbGx5IHNwZWFraW5nLCBgcHVycnI6Om1hcCgpYCBjYW4gYmUgdXNlZCB0byBpdGVyYXRlIG92ZXIgdGhpcyBsaXN0IHRvIHZpc3VhbGl6ZSBvciBhbmFseXplIGVhY2ggY2x1c3RlcmluZyByZXN1bHQgb24gaXRzIG93bjsgd2UnbGwgdXNlIHRoaXMgYXBwcm9hY2ggaW4gdGhlIGZvbGxvd2luZyBzZWN0aW9ucy4gCgojIyMgVmlzdWFsaXppbmcgY2x1c3RlcmluZyByZXN1bHRzCgpXaGVuIGNvbXBhcmluZyBjbHVzdGVyaW5nIHJlc3VsdHMsIGl0J3MgaW1wb3J0YW50IHRvIGZpcnN0IHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW50IGNsdXN0ZXJpbmdzIHRvIGJ1aWxkIGNvbnRleHQgZm9yIGludGVycHJldGluZyBRQyBtZXRyaWNzLgoKQXMgb25lIGV4YW1wbGUgb2Ygd2h5IHRoaXMgaXMgaW1wb3J0YW50LCB3ZSBnZW5lcmFsbHkgZXhwZWN0IHRoYXQgbW9yZSByb2J1c3QgY2x1c3RlcnMgd2lsbCBoYXZlIGhpZ2hlciB2YWx1ZXMgZm9yIG1ldHJpY3MgbGlrZSBzaWxob3VldHRlIHdpZHRoIGFuZCBuZWlnaGJvcmhvb2QgcHVyaXR5LgpIb3dldmVyLCB3ZSBhbHNvIGV4cGVjdCB0aGF0IGhhdmluZyBmZXdlciBjbHVzdGVycyBpbiB0aGUgZmlyc3QgcGxhY2Ugd2lsbCBhbHNvIGxlYWQgdG8gaGlnaGVyIG1ldHJpY3MsIHJlZ2FyZGxlc3Mgb2YgY2x1c3RlciBxdWFsaXR5OiBXaGVuIHRoZXJlIGFyZSBmZXdlciBjbHVzdGVycywgaXQgaXMgbW9yZSBsaWtlbHkgdGhhdCBjbHVzdGVycyBvdmVybGFwIGxlc3Mgd2l0aCBvbmUgYW5vdGhlciBqdXN0IGJlY2F1c2UgdGhlcmUgYXJlbid0IG1hbnkgY2x1c3RlcnMgaW4gdGhlIGZpcnN0IHBsYWNlLgpUaGlzIG1lYW5zIHRoYXQsIHdoZW4gaW50ZXJwcmV0aW5nIGNsdXN0ZXIgcXVhbGl0eSBtZXRyaWNzLCB5b3Ugc2hvdWxkIGJlIGNhcmVmdWwgdG8gdGFrZSBtb3JlIGNvbnRleHQgYWJvdXQgdGhlIGRhdGEgaW50byBjb25zaWRlcmF0aW9uIGFuZCBub3Qgb25seSByZWx5IG9uIHRoZSBtZXRyaWMgdmFsdWVzLgoKV2UnbGwgdGhlcmVmb3JlIHZpc3VhbGl6ZSB0aGVzZSByZXN1bHRzIGFzIFVNQVBzIGJ5IGl0ZXJhdGluZyBvdmVyIGBjbHVzdGVyX3Jlc3VsdHNfbGlzdGAgYW5kIGNvbWJpbmluZyBwbG90cyB3aXRoIFtgcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKClgXShodHRwczovL3BhdGNod29yay5kYXRhLWltYWdpbmlzdC5jb20vcmVmZXJlbmNlL3dyYXBfcGxvdHMuaHRtbCkuCldlJ2xsIHNwZWNpZmljYWxseSB1c2UgW2BwdXJycjo6aW1hcCgpYF0oaHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbWFwLmh0bWwpIHRvIGl0ZXJhdGUgc28gdGhhdCB3ZSBjYW4gYXNzaWduIHRoaXMgbGlzdCdzIG5hbWVzIGFzIHBsb3QgdGl0bGVzLgoKRm9yIHRoaXMsIHdlJ2xsIGJlZ2luIGJ5IGV4dHJhY3RpbmcgYSB0YWJsZSBvZiBVTUFQIGNvb3JkaW5hdGVzIGZyb20gb3VyIFNDRSBvYmplY3QuCgpgYGB7ciBjcmVhdGUgdW1hcF9kZn0KdW1hcF9kZiA8LSByZWR1Y2VkRGltKHNjZSwgIlVNQVAiKSB8PgogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCk5leHQsIHdlJ2xsIGl0ZXJhdGUgb3ZlciBgY2x1c3Rlcl9yZXN1bHRzX2xpc3RgIHRvIHBsb3QgdGhlIFVNQVBzLgoKYGBge3IgcGxvdCBubiB1bWFwcywgZmlnLndpZHRoID0gMTJ9CnVtYXBfcGxvdHMgPC0gY2x1c3Rlcl9yZXN1bHRzX2xpc3QgfD4KICBwdXJycjo6aW1hcCgKICAgIFwoY2x1c3Rlcl9kZiwgY2x1c3RlcmluZ19uYW1lKSB7CiAgICAgICMgQWRkIGEgY29sdW1uIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyB0byB1bWFwX2RmCiAgICAgIHVtYXBfZGZfcGxvdCA8LSB1bWFwX2RmIHw+CiAgICAgICAgZHBseXI6Om11dGF0ZShjbHVzdGVyID0gY2x1c3Rlcl9kZiRjbHVzdGVyKQoKICAgICAgIyBQbG90IHRoZSBVTUFQLCBjb2xvcmVkIGJ5IHRoZSBuZXcgY2x1c3RlciB2YXJpYWJsZQogICAgICBnZ3Bsb3QodW1hcF9kZl9wbG90LCBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gY2x1c3RlcikpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArCiAgICAgICAgbGFicyh0aXRsZSA9IGdsdWU6OmdsdWUoIm5lYXJlc3QgbmVpZ2hib3JzOiB7Y2x1c3RlcmluZ19uYW1lfSIpKSArCiAgICAgICAgIyBXZSdsbCBhZGQgYSBjb3VwbGUgVU1BUCBwbG90IHNldHRpbmdzIGhlcmUsIGluY2x1ZGluZyBlcXVhbCBheGVzIGFuZAogICAgICAgICMgdHVybmluZyBvZmYgdGhlIGF4aXMgdGlja3MgYW5kIHRleHQgc2luY2UgVU1BUCBjb29yZGluYXRlcyBhcmUgbm90IG1lYW5pbmdmdWwKICAgICAgICBjb29yZF9lcXVhbCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICApCiAgICB9CiAgKQoKIyBQcmludCB0aGUgcGxvdHMgd2l0aCBwYXRjaHdvcms6OndyYXBfcGxvdHMoKQpwYXRjaHdvcms6OndyYXBfcGxvdHModW1hcF9wbG90cykKYGBgCgpUaGVzZSBwbG90cyBzaG93IHRoYXQgdGhlIG51bWJlciBvZiBjbHVzdGVycyBkZWNyZWFzZXMgYXMgdGhlIG5lYXJlc3QgbmVpZ2hib3JzIHBhcmFtZXRlciBpbmNyZWFzZXMsIHdpdGggYmV0d2VlbiA5LTEzIGNsdXN0ZXJzLgoKCgoKIyMjIEV2YWx1YXRpbmcgY2x1c3RlcmluZyByZXN1bHRzCgpUaGlzIHNlY3Rpb24gd2lsbCB1c2UgYHB1cnJyOjptYXAoKWAgdG8gaXRlcmF0ZSBvdmVyIGVhY2ggY2x1c3RlcmluZyByZXN1bHQgZGF0YSBmcmFtZSB0byBjYWxjdWxhdGUgc2lsaG91ZXR0ZSB3aWR0aCwgbmVpZ2hib3Job29kIHB1cml0eSwgYW5kIHN0YWJpbGl0eSwgYW5kIHRoZW4gdmlzdWFsaXplIHJlc3VsdHMuClRoZSBnb2FsIG9mIHRoaXMgY29kZSBpcyB0byBpZGVudGlmeSB3aGV0aGVyIG9uZSBjbHVzdGVyaW5nIHBhcmFtZXRlcml6YXRpb24gcHJvZHVjZXMgbW9yZSByZWxpYWJsZSBjbHVzdGVycy4gCgoKIyMjIyBTaWxob3VldHRlIHdpZHRoIGFuZCBuZWlnaGJvcmhvb2QgcHVyaXR5CgpCb3RoIHNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoYm9yaG9vZCBwdXJpdHkgYXJlIGNlbGwtbGV2ZWwgcXVhbnRpdGllcywgc28gd2UgY2FuIGNhbGN1bGF0ZSB0aGVtIHRvZ2V0aGVyIGluIHRoZSBzYW1lIGNhbGwgdG8gYHB1cnJyOjptYXAoKWAuCkJlbG93LCB3ZSdsbCBpdGVyYXRlIG92ZXIgZWFjaCBkYXRhIGZyYW1lIGluIGBjbHVzdGVyX3Jlc3VsdHNfbGlzdGAgdG8gY2FsY3VsYXRlIHRoZXNlIHF1YW50aXRpZXMuCgpgYGB7ciBjYWxjdWxhdGUgY2VsbCBsZXZlbCBtZXRyaWNzfQpjZWxsX21ldHJpY19saXN0IDwtIGNsdXN0ZXJfcmVzdWx0c19saXN0IHw+CiAgcHVycnI6Om1hcCgKICAgIFwoY2x1c3Rlcl9kZikgewogICAgICAjIGNhbGN1bGF0ZSBzaWxob3VldHRlIHdpZHRoCiAgICAgIHNpbGhvdWV0dGVfZGYgPC0gck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3NpbGhvdWV0dGUocGNhX21hdHJpeCwgY2x1c3Rlcl9kZikKCiAgICAgICMgY2FsY3VsYXRlIG5laWdoYmhvcmhvb2QgcHVyaXR5CiAgICAgIHB1cml0eV9kZiA8LSByT3BlblNjUENBOjpjYWxjdWxhdGVfcHVyaXR5KHBjYV9tYXRyaXgsIGNsdXN0ZXJfZGYpCgogICAgICAjIENvbWJpbmUgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oc2lsaG91ZXR0ZV9kZiwgcHVyaXR5X2RmKQogICAgfQogICkKCiMgVmlldyB0aGUgZmlyc3Qgc2l4IHJvd3Mgb2YgZWFjaCBjbHVzdGVyaW5nIHJlc3VsdCdzIGNlbGwtbGV2ZWwgUUMgbWV0cmljcwpwdXJycjo6bWFwKGNlbGxfbWV0cmljX2xpc3QsIGhlYWQpCmBgYAoKClRvIHZpc3VhbGl6ZSB0aGVzZSByZXN1bHRzLCB3ZSBjYW4gY29tYmluZSBhbGwgZGF0YSBmcmFtZXMgaW4gdGhpcyBsaXN0IGludG8gYSBzaW5nbGUgb3ZlcmFsbCBkYXRhIGZyYW1lLCB3aGVyZSB0aGUgZXhpc3RpbmcgYG5uYCBjb2x1bW4gd2lsbCBkaXN0aW5ndWlzaCBhbW9uZyBjb25kaXRpb25zLgoKYGBge3IgY29tYmluZSBjZWxsIG1ldHJpY3MgbGlzdH0KY2VsbF9tZXRyaWNzX2RmIDwtIGRwbHlyOjpiaW5kX3Jvd3MoY2VsbF9tZXRyaWNfbGlzdCkKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoYm9yaG9vZCBwdXJpdHkgZWFjaCB3aXRoIGJveHBsb3RzLCBmb3IgZXhhbXBsZSwgYW5kIHVzZSB0aGUgW2BwYXRjaHdvcmtgXShodHRwczovL3BhdGNod29yay5kYXRhLWltYWdpbmlzdC5jb20vKSBwYWNrYWdlIHRvIHByaW50IHRoZW0gdG9nZXRoZXI6CgoKYGBge3J9CiMgUGxvdCBzaWxob3VldHRlIHdpZHRoCnNpbGhvdWV0dGVfcGxvdCA8LSBnZ3Bsb3QoY2VsbF9tZXRyaWNzX2RmKSArCiAgYWVzKHggPSBhcy5mYWN0b3Iobm4pLCB5ID0gc2lsaG91ZXR0ZV93aWR0aCwgZmlsbCA9IGFzLmZhY3RvcihubikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwyIikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyIsCiAgICB5ID0gIlNpbGhvdWV0dGUgd2lkdGgiCiAgKQoKCiMgUGxvdCBuZWlnaGJvcmhvb2QgcHVyaXR5IHdpZHRoCnB1cml0eV9wbG90IDwtIGdncGxvdChjZWxsX21ldHJpY3NfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBwdXJpdHksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMiLAogICAgeSA9ICJOZWlnaGJvcmhvb2QgcHVyaXR5IgogICkKCgojIEFkZCB0b2dldGhlciB1c2luZyB0aGUgcGF0Y2h3b3JrIGxpYnJhcnksIHdpdGhvdXQgYSBsZWdlbmQKc2lsaG91ZXR0ZV9wbG90ICsgcHVyaXR5X3Bsb3QgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKV2hpbGUgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGEgc2FsaWVudCBkaWZmZXJlbmNlIGFtb25nIHNpbGhvdWV0dGUgd2lkdGggZGlzdHJpYnV0aW9ucywgaXQgZG9lcyBhcHBlYXIgdGhhdCBwdXJpdHkgaXMgaGlnaGVyIHdpdGggYSBoaWdoZXIgbmVhcmVzdCBuZWlnaGJvcnMgcGFyYW1ldGVyLgoKCiMjIyMgU3RhYmlsaXR5CgpOZXh0LCB3ZSdsbCBjYWxjdWxhdGUgc3RhYmlsaXR5IG9uIHRoZSBjbHVzdGVycyB1c2luZyBgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3N0YWJpbGl0eSgpYCwgc3BlY2lmeWluZyB0aGUgc2FtZSBwYXJhbWV0ZXIgdXNlZCBmb3IgdGhlIG9yaWdpbmFsIGNsdXN0ZXIgY2FsY3VsYXRpb24gYXQgZWFjaCBpdGVyYXRpb24uIAoKYGBge3IgY2FsY3VsYXRlIHN0YWJpbGl0eX0Kc3RhYmlsaXR5X2xpc3QgPC0gY2x1c3Rlcl9yZXN1bHRzX2xpc3QgfD4KICBwdXJycjo6bWFwKAogICAgXChjbHVzdGVyX2RmKSB7CiAgICAgIG5uIDwtIHVuaXF1ZShjbHVzdGVyX2RmJG5uKQoKICAgICAgIyBjYWxjdWxhdGUgc3RhYmlsaXR5LCBwYXNzaW5nIGluIHRoZSBwYXJhbWV0ZXIgdmFsdWUgdXNlZCBmb3IgdGhpcyBpdGVyYXRpb24KICAgICAgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3N0YWJpbGl0eShwY2FfbWF0cml4LCBjbHVzdGVyX2RmLCBubiA9IG5uKQogICAgfQogICkKYGBgCgpXZSdsbCBhZ2FpbiBjb21iaW5lIHRoZSBvdXRwdXQgb2YgYHN0YWJpbGl0eV9saXN0YCBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgYW5kIHBsb3QgYGFyaWAgdmFsdWVzIGFjcm9zcyBgbm5gIHBhcmFtZXRlcml6YXRpb25zLgoKCmBgYHtyIGNvbWJpbmUgcGxvdCBzdGFiaWxpdHl9CnN0YWJpbGl0eV9kZiA8LSBkcGx5cjo6YmluZF9yb3dzKHN0YWJpbGl0eV9saXN0KQoKZ2dwbG90KHN0YWJpbGl0eV9kZikgKwogIGFlcyh4ID0gYXMuZmFjdG9yKG5uKSwgeSA9IGFyaSwgZmlsbCA9IGFzLmZhY3RvcihubikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwyIikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyIsCiAgICB5ID0gIkFkanVzdGVkIFJhbmQgSW5kZXgiCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkhlcmUsIHdlIHNlZSB0aGF0IGEgbmVhcmVzdCBuZWlnaGJvcnMgdmFsdWUgb2YgMjAgb3IgMzAgbGVhZHMgdG8gbW9yZSBzdGFibGUgY2x1c3RlcmluZyByZXN1bHRzIGNvbXBhcmVkIHRvIDEwLiAKCgojIyBWYXJ5aW5nIG11bHRpcGxlIGNsdXN0ZXJpbmcgcGFyYW1ldGVycwoKVGhlIHByZXZpb3VzIHNlY3Rpb24gZGVtb25zdHJhdGVkIGhvdyB0byBjYWxjdWxhdGUgY2x1c3RlcnMgYW5kIFFDIG1ldHJpY3Mgd2hlbiB2YXJ5aW5nIG9uZSBwYXJhbWV0ZXIsIGJ1dCBpdCBpcyBwb3NzaWJsZSB0byB2YXJ5IG11bHRpcGxlIHBhcmFtZXRlcnMgYXQgb25jZSB3aXRoIGByT3BlblNjUENBOjpzd2VlcF9jbHVzdGVycygpYC4KSW4gdGhpcyBzZWN0aW9uLCB3ZSdsbCBzaG93IGFuIG92ZXJ2aWV3IG9mIGhvdyB5b3UgbWlnaHQgd3JpdGUgY29kZSB0byB2YXJ5IHR3byBwYXJhbWV0ZXJzIGF0IG9uY2UgKGhlcmUsIG5lYXJlc3QgbmVpZ2hib3JzIGFuZCByZXNvbHV0aW9uIGFzIGV4YW1wbGVzKSBhbmQgdmlzdWFsaXplIHJlc3VsdHMuCgoKYGBge3Igc3dlZXAgdHdvIHBhcmFtZXRlcnN9CiMgRGVmaW5lIHZlY3RvcnMgb2YgcGFyYW1ldGVycyB0byB2YXJ5Cm5uX3ZhbHVlcyA8LSBzZXEoMTAsIDMwLCAxMCkKcmVzX3ZhbHVlcyA8LSBzZXEoNSwgMTUsIDUpIC8gMTAKCgpjbHVzdGVyX3Jlc3VsdHNfbGlzdCA8LSByT3BlblNjUENBOjpzd2VlcF9jbHVzdGVycygKICBwY2FfbWF0cml4LAogIG5uID0gbm5fdmFsdWVzLAogIHJlc29sdXRpb24gPSByZXNfdmFsdWVzCikKYGBgCgpUaGlzIHJlc3VsdGluZyBsaXN0IG5vdyBoYXMgOSBkaWZmZXJlbnQgY2x1c3RlcmluZyByZXN1bHRzLCBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZiBgbm5fdmFsdWVzYCBhbmQgYHJlc192YWx1ZXNgOgoKCmBgYHtyIGxlbmd0aCBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB0d28gcGFyYW1ldGVyc30KbGVuZ3RoKGNsdXN0ZXJfcmVzdWx0c19saXN0KQpgYGAKCgojIyMgVmlzdWFsaXplIGNsdXN0ZXJzCgpOZXh0LCB3ZSdsbCBpdGVyYXRlIG92ZXIgYGNsdXN0ZXJfcmVzdWx0c19saXN0YCB0byBwbG90IHRoZSBVTUFQcy4KVGhpcyB0aW1lLCB3ZSdsbCB1c2UgYHB1cnJyOjptYXAoKWAgYW5kIHB1bGwgb3V0IHBhcmFtZXRlcnMgZnJvbSBlYWNoIGl0ZXJhdGlvbidzIGBjbHVzdGVyX2RmYCB0byBmb3JtIHRoZSBVTUFQIHBhbmVsIHRpdGxlLgoKYGBge3IgcGxvdCBubiByZXMgdW1hcHMsIGZpZy5oZWlnaHQgPSAxNH0KdW1hcF9wbG90cyA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgIyBBZGQgYSBjb2x1bW4gd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIHRvIHVtYXBfZGYKICAgICAgdW1hcF9kZl9wbG90IDwtIHVtYXBfZGYgfD4KICAgICAgICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyX2RmJGNsdXN0ZXIpCgogICAgICAjIENyZWF0ZSBhIHRpdGxlIGZvciB0aGUgVU1BUCB3aXRoIGJvdGggcGFyYW1ldGVycwogICAgICB1bWFwX3RpdGxlIDwtIGdsdWU6OmdsdWUoCiAgICAgICAgIm5uOiB7dW5pcXVlKGNsdXN0ZXJfZGYkbm4pfTsgcmVzOiB7dW5pcXVlKGNsdXN0ZXJfZGYkcmVzb2x1dGlvbil9IgogICAgICApCgogICAgICAjIFBsb3QgdGhlIFVNQVAsIGNvbG9yZWQgYnkgdGhlIG5ldyBjbHVzdGVyIHZhcmlhYmxlCiAgICAgIGdncGxvdCh1bWFwX2RmX3Bsb3QsIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBjbHVzdGVyKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICAgICAgICBsYWJzKHRpdGxlID0gdW1hcF90aXRsZSkgKwogICAgICAgICMgV2UnbGwgYWRkIGEgY291cGxlIFVNQVAtc3BlY2lmaWMgcGxvdCBzZXR0aW5ncwogICAgICAgIGNvb3JkX2VxdWFsKCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICAgICAgKQogICAgfQogICkKCiMgUHJpbnQgdGhlIHBsb3RzIHdpdGggcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKCkKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHVtYXBfcGxvdHMpCmBgYAoKCgojIyMgQ2FsY3VsYXRlIGFuZCB2aXN1YWxpemUgUUMgbWV0cmljcwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIG9uZSBjb2Rpbmcgc3RyYXRlZ3kgdG8gY2FsY3VsYXRlIGFuZCB2aXN1YWxpemUgcmVzdWx0cyB3aGVuIHZhcnlpbmcgdHdvIGNsdXN0ZXJpbmcgcGFyYW1ldGVycy4KSW4gcGFydGljdWxhciwgd2UgdXNlIGZhY2V0aW5nIHRvIGhlbHAgZGlzcGxheSBhbGwgaW5mb3JtYXRpb24gaW4gb25lIHBsb3QsIGJ5IHBsYWNpbmcgbmVhcmVzdCBuZWlnaGJvciB2YWx1ZXMgb24gdGhlIFgtYXhpcyBhbmQgZmFjZXRpbmcgYnkgcmVzb2x1dGlvbiB2YWx1ZXMuClNpbmNlIHNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoaG9yYm9vZCBwdXJpdHkgY2FsY3VsYXRpb25zIHVzaW5nIGdlbmVyYWxseSBzaW1pbGFyIGNvZGUsIHdlJ2xsIGp1c3Qgc2hvdyBuZWlnaGJvcmhvb2QgcHVyaXR5IGhlcmUuCgojIyMjIE5laWdoYm9yaG9vZCBwdXJpdHkKCkZpcnN0LCB3ZSdsbCBjYWxjdWxhdGUgbmVpZ2hib3Job29kIHB1cml0eSBhbmQgY29tYmluZSByZXN1bHRzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZS4KCmBgYHtyIGNhbGN1bGF0ZSBwdXJpdHkgdHdvIHBhcmFtZXRlcnN9CnB1cml0eV9kZiA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3B1cml0eShwY2FfbWF0cml4LCBjbHVzdGVyX2RmKQogICAgfQogICkgfD4KICBkcGx5cjo6YmluZF9yb3dzKCkKYGBgCgoKV2UnbGwgYWRkIGEgY29sdW1uIGByZXNvbHV0aW9uX2xhYmVsYCB3aGljaCB3ZSdsbCB1c2UgdG8gaGF2ZSBpbmZvcm1hdGl2ZSBwYW5lbCB0aXRsZXMgaW4gdGhlIGZhY2V0ZWQgZ2dwbG90IHdlIG1ha2UgbmV4dC4KCmBgYHtyIGFkZCByZXNvbHV0aW9uX2xhYmVsIGNvbHVtbn0KcHVyaXR5X2RmIDwtIHB1cml0eV9kZiB8PgogIGRwbHlyOjptdXRhdGUocmVzb2x1dGlvbl9sYWJlbCA9IGdsdWU6OmdsdWUoIlJlc29sdXRpb246IHtyZXNvbHV0aW9ufSIpKQpgYGAKCmBgYHtyIHZpc3VhbGl6ZSBwdXJpdHkgdHdvIHBhcmFtZXRlcnN9CmdncGxvdChwdXJpdHlfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBwdXJpdHksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICAjIGZhY2V0IGJ5IHJlc29sdXRpb24KICBmYWNldF93cmFwKHZhcnMocmVzb2x1dGlvbl9sYWJlbCkpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMiLAogICAgeSA9ICJOZWlnaGJvcmhvb2QgcHVyaXR5IgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyMgU3RhYmlsaXR5CgpTaW1pbGFyIHRvIGFib3ZlLCB3ZSdsbCBjYWxjdWxhdGUgc3RhYmlsaXR5LCBjb21iaW5lIHJlc3VsdHMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lLCBhZGQgYSBgcmVzb2x1dGlvbl9sYWJlbGAgY29sdW1uIHRvIHN1cHBvcnQgcGxvdCBpbnRlcnByZXRhdGlvbiwgYW5kIGZpbmFsbHkgbWFrZSBvdXIgcGxvdC4KCmBgYHtyIGNhbGN1bGF0ZSBzdGFiaWxpdHkgdHdvIHBhcmFtZXRlcnN9CnN0YWJpbGl0eV9kZiA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgIyBFeHRyYWN0IHBhcmFtZXRlcnMgZm9yIHRoaXMgY2x1c3RlcmluZyByZXN1bHQKICAgICAgbm4gPC0gdW5pcXVlKGNsdXN0ZXJfZGYkbm4pCiAgICAgIHJlc29sdXRpb24gPC0gdW5pcXVlKGNsdXN0ZXJfZGYkcmVzb2x1dGlvbikKCiAgICAgIHJPcGVuU2NQQ0E6OmNhbGN1bGF0ZV9zdGFiaWxpdHkoCiAgICAgICAgcGNhX21hdHJpeCwKICAgICAgICBjbHVzdGVyX2RmLAogICAgICAgIG5uID0gbm4sCiAgICAgICAgcmVzb2x1dGlvbiA9IHJlc29sdXRpb24KICAgICAgKQogICAgfQogICkgfD4KICBkcGx5cjo6YmluZF9yb3dzKCkKCnN0YWJpbGl0eV9kZiA8LSBzdGFiaWxpdHlfZGYgfD4KICBkcGx5cjo6bXV0YXRlKHJlc29sdXRpb25fbGFiZWwgPSBnbHVlOjpnbHVlKCJSZXNvbHV0aW9uOiB7cmVzb2x1dGlvbn0iKSkKYGBgCgoKYGBge3IgdmlzdWFsaXplIHN0YWJpbGl0eSB0d28gcGFyYW1ldGVyc30KZ2dwbG90KHN0YWJpbGl0eV9kZikgKwogIGFlcyh4ID0gYXMuZmFjdG9yKG5uKSwgeSA9IGFyaSwgZmlsbCA9IGFzLmZhY3RvcihubikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwyIikgKwogICMgZmFjZXQgYnkgcmVzb2x1dGlvbgogIGZhY2V0X3dyYXAodmFycyhyZXNvbHV0aW9uX2xhYmVsKSkgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyIsCiAgICB5ID0gIkFkanVzdGVkIFJhbmQgSW5kZXgiCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7ciBzZXNzaW9uIGluZm99CiMgcmVjb3JkIHRoZSB2ZXJzaW9ucyBvZiB0aGUgcGFja2FnZXMgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGFuZCBvdGhlciBlbnZpcm9ubWVudCBpbmZvcm1hdGlvbgpzZXNzaW9uSW5mbygpCmBgYAo=
+LS0tCnRpdGxlOiAiQ29tcGFyaW5nIGNsdXN0ZXJpbmcgcGFyYW1ldGVycyB3aXRoIHJPcGVuU2NQQ0EiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYXV0aG9yOiAiRGF0YSBMYWIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkNsdXN0ZXJpbmcgYWxnb3JpdGhtcyBoYXZlIHNldmVyYWwgcGFyYW1ldGVycyB3aGljaCBjYW4gYmUgdmFyaWVkLCBsZWFkaW5nIHRvIGRpZmZlcmVudCBjbHVzdGVyaW5nIHJlc3VsdHMuCkEga2V5IHF1ZXN0aW9uIHdoZW4gY2x1c3RlcmluZywgdGhlcmVmb3JlLCBpcyBob3cgdG8gaWRlbnRpZnkgYSBzZXQgb2YgcGFyYW1ldGVycyB0aGF0IGxlYWQgdG8gcm9idXN0IGFuZCByZWxpYWJsZSBjbHVzdGVycyB0aGF0IGNhbiBiZSB1c2VkIGluIGRvd25zdHJlYW0gYW5hbHlzaXMuIAoKVGhpcyBub3RlYm9vayBwcm92aWRlcyBleGFtcGxlcyBvZiBob3cgdG8gdXNlIHRoZSBgck9wZW5TY1BDQWAgcGFja2FnZSB0bzoKCiogQ2FsY3VsYXRlIHNldmVyYWwgdmVyc2lvbnMgb2YgY2x1c3RlcmluZyByZXN1bHRzIGFjcm9zcyBzZXZlcmFsIGRpZmZlcmVudCBwYXJhbWV0ZXJpemF0aW9ucwoqIENhbGN1bGF0ZSBRQyBtZXRyaWNzIG9uIGFjcm9zcyBjbHVzdGVyaW5nIHJlc3VsdHMKClBsZWFzZSByZWZlciB0byB0aGUgW2AwMV9wZXJmb3JtLWV2YWx1YXRlLWNsdXN0ZXJpbmcuUm1kYF0oMDFfcGVyZm9ybS1ldmFsdWF0ZS1jbHVzdGVyaW5nLlJtZCkgbm90ZWJvb2sgZm9yIGEgdHV0b3JpYWwgb24gdXNpbmcgYHJPcGVuU2NQQ0FgIGZ1bmN0aW9ucyB0bzoKCiogQ2FsY3VsYXRlIGNsdXN0ZXJzIGZyb20gYSBzaW5nbGUgcGFyYW1ldGVyaXphdGlvbgoqIENhbGN1bGF0ZSBRQyBtZXRyaWNzIG9uIGEgc2luZ2xlIHNldCBvZiBjbHVzdGVycywgYXMgd2VsbCBhcyBleHBsYW5hdGlvbnMgb2YgdGhlIG1ldHJpY3MgdGhlbXNlbHZlcwoKVGhpcyBub3RlYm9vayB3aWxsIHVzZSB0aGUgc2FtcGxlIGBTQ1BDUzAwMDAwMWAgZnJvbSBwcm9qZWN0IGBTQ1BDUDAwMDAwMWAsIHdoaWNoIGlzIGFzc3VtZWQgcHJlc2VudCBpbiB0aGUgYE9wZW5TY1BDQS1hbmFseXNpcy9kYXRhL2N1cnJlbnQvU0NQQ1AwMDAwMDFgIGRpcmVjdG9yeSwgZm9yIGFsbCBleGFtcGxlcy4KUGxlYXNlIFtzZWUgdGhpcyBkb2N1bWVudGF0aW9uXShodHRwczovL29wZW5zY3BjYS5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvZ2V0dGluZy1zdGFydGVkL2FjY2Vzc2luZy1yZXNvdXJjZXMvZ2V0dGluZy1hY2Nlc3MtdG8tZGF0YS8pIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IG9idGFpbmluZyBTY1BDQSBkYXRhLgoKIyMgU2V0dXAKCiMjIyBQYWNrYWdlcwoKCmBgYHtyIHBhY2thZ2VzfQpsaWJyYXJ5KHJPcGVuU2NQQ0EpCgpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkocGF0Y2h3b3JrKQp9KQoKIyBTZXQgZ2dwbG90IHRoZW1lIGZvciBwbG90cwp0aGVtZV9zZXQodGhlbWVfYncoKSkKYGBgCgoKIyMjIFBhdGhzCgpgYGB7ciBiYXNlIHBhdGhzfQojIFRoZSBiYXNlIHBhdGggZm9yIHRoZSBPcGVuU2NQQ0EgcmVwb3NpdG9yeQpyZXBvc2l0b3J5X2Jhc2UgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0aHViIikpCgojIFRoZSBjdXJyZW50IGRhdGEgZGlyZWN0b3J5LCBmb3VuZCB3aXRoaW4gdGhlIHJlcG9zaXRvcnkgYmFzZSBkaXJlY3RvcnkKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHJlcG9zaXRvcnlfYmFzZSwgImRhdGEiLCAiY3VycmVudCIpCgojIFRoZSBwYXRoIHRvIHRoaXMgbW9kdWxlCm1vZHVsZV9iYXNlIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJhbmFseXNlcyIsICJoZWxsby1jbHVzdGVycyIpCmBgYAoKYGBge3IgaW5wdXQgZmlsZSBwYXRofQojIFBhdGggdG8gcHJvY2Vzc2VkIFNDRSBmaWxlIGZvciBzYW1wbGUgU0NQQ1MwMDAwMDEKaW5wdXRfc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiU0NQQ1AwMDAwMDEiLCAiU0NQQ1MwMDAwMDEiLCAiU0NQQ0wwMDAwMDFfcHJvY2Vzc2VkLnJkcyIpCmBgYAoKCiMjIyBTZXQgdGhlIHJhbmRvbSBzZWVkCgpCZWNhdXNlIGNsdXN0ZXJpbmcgaW52b2x2ZXMgcmFuZG9tIHNhbXBsaW5nLCBpdCBpcyBpbXBvcnRhbnQgdG8gc2V0IHRoZSByYW5kb20gc2VlZCBhdCB0aGUgdG9wIG9mIHlvdXIgYW5hbHlzaXMgc2NyaXB0IG9yIG5vdGVib29rIHRvIGVuc3VyZSByZXByb2R1Y2liaWxpdHkuCgpgYGB7ciBzZXQgc2VlZH0Kc2V0LnNlZWQoMjAyNCkKYGBgCgojIyBSZWFkIGluIGFuZCBwcmVwYXJlIGRhdGEKClRvIGJlZ2luLCB3ZSdsbCByZWFkIGluIHRoZSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIChTQ0UpIG9iamVjdC4KCmBgYHtyIHJlYWQgZGF0YX0KIyBSZWFkIHRoZSBTQ0UgZmlsZQpzY2UgPC0gcmVhZFJEUyhpbnB1dF9zY2VfZmlsZSkKYGBgCgpGb3IgdGhlIGluaXRpYWwgY2x1c3RlciBjYWxjdWxhdGlvbnMgYW5kIGV2YWx1YXRpb25zLCB3ZSB3aWxsIHVzZSB0aGUgUENBIG1hdHJpeCBleHRyYWN0ZWQgZnJvbSB0aGUgU0NFIG9iamVjdC4KQXMgc2hvd24gaW4gW2AwMV9wZXJmb3JtLWV2YWx1YXRlLWNsdXN0ZXJpbmcuUm1kYF0oMDFfcGVyZm9ybS1ldmFsdWF0ZS1jbHVzdGVyaW5nLlJtZCksIGl0IGlzIGFsc28gcG9zc2libGUgdG8gdXNlIGFuIFNDRSBvYmplY3Qgb3IgYSBTZXVyYXQgb2JqZWN0IGRpcmVjdGx5LgoKCmBgYHtyIGV4dHJhY3QgcGNhIGRhdGF9CiMgRXh0cmFjdCB0aGUgUENBIG1hdHJpeCBmcm9tIGFuIFNDRSBvYmplY3QKcGNhX21hdHJpeCA8LSByZWR1Y2VkRGltKHNjZSwgIlBDQSIpCmBgYAoKIyMgVmFyeWluZyBhIHNpbmdsZSBjbHVzdGVyaW5nIHBhcmFtZXRlcgoKVGhpcyBzZWN0aW9uIHdpbGwgc2hvdyBob3cgdG8gcGVyZm9ybSBjbHVzdGVyaW5nIGFjcm9zcyBhIHNldCBvZiBwYXJhbWV0ZXJzIChha2EsICJzd2VlcCIgYSBzZXQgb2YgcGFyYW1ldGVycykgd2l0aCBgck9wZW5TY1BDQTo6c3dlZXBfY2x1c3RlcnMoKWAuIAoKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIFBDQSBtYXRyaXggd2l0aCByb3cgbmFtZXMgcmVwcmVzZW50aW5nIHVuaXF1ZSBjZWxsIGlkcyAoZS5nLiwgYmFyY29kZXMpIGFzIGl0cyBwcmltYXJ5IGFyZ3VtZW50LCB3aXRoIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBjbHVzdGVyIHBhcmFtZXRlcnMuIApUaGlzIGZ1bmN0aW9uIHdyYXBzIHRoZSBgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX2NsdXN0ZXJzKClgIGZ1bmN0aW9uIGJ1dCBhbGxvd3MgeW91IHRvIHByb3ZpZGUgYSB2ZWN0b3Igb2YgcGFyYW1ldGVyIHZhbHVlcyB0byBwZXJmb3JtIGNsdXN0ZXJpbmcgYWNyb3NzLCBhcyBsaXN0ZWQgYmVsb3cuCkNsdXN0ZXJzIHdpbGwgYmUgY2FsY3VsYXRlZCBmb3IgYWxsIGNvbWJpbmF0aW9ucyBvZiBwYXJhbWV0ZXJzIHZhbHVlcyAod2hlcmUgYXBwbGljYWJsZSk7IGRlZmF1bHQgdmFsdWVzIHRoYXQgdGhlIGZ1bmN0aW9uIHdpbGwgdXNlIGZvciBhbnkgdW5zcGVjaWZpZWQgcGFyYW1ldGVyIHZhbHVlcyBhcmUgc2hvd24gaW4gcGFyZW50aGVzZXMuCgoqIGBhbGdvcml0aG1gOiBXaGljaCBjbHVzdGVyaW5nIGFsZ29yaXRobSB0byB1c2UgKExvdXZhaW4pCiogYHdlaWdodGluZ2A6IFdoaWNoIHdlaWdodGluZyBzY2hlbWUgdG8gdXNlIChKYWNjYXJkKQoqIGBubmA6IFRoZSBudW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMgKDEwKQoqIGByZXNvbHV0aW9uYDogVGhlIHJlc29sdXRpb24gcGFyYW1ldGVyICgxOyB1c2VkIG9ubHkgd2l0aCBMb3V2YWluIGFuZCBMZWlkZW4gY2x1c3RlcmluZykKKiBgb2JqZWN0aXZlX2Z1bmN0aW9uYDogVGhlIG9iamVjdGl2ZSBmdW5jdGlvbiB0byBvcHRpbWl6ZSBjbHVzdGVycyAoQ1BNOyB1c2VkIG9ubHkgd2l0aCBMZWlkZW4gY2x1c3RlcmluZykKCmByT3BlblNjUENBOjpzd2VlcF9jbHVzdGVycygpYCBkb2VzIG5vdCBhbGxvdyB5b3UgdG8gc3BlY2lmeSB2YWx1ZXMgZm9yIGFueSBvdGhlciBwYXJhbWV0ZXJzLiAKCgpUaGlzIGZ1bmN0aW9uIHdpbGwgcmV0dXJuIGEgbGlzdCBvZiBkYXRhIGZyYW1lcyBvZiBjbHVzdGVyaW5nIHJlc3VsdHMuIApFYWNoIGRhdGEgZnJhbWUgd2lsbCBoYXZlIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCiogYGNlbGxfaWRgOiBVbmlxdWUgY2VsbCBpZGVudGlmaWVycywgb2J0YWluZWQgZnJvbSB0aGUgUENBIG1hdHJpeCdzIHJvdyBuYW1lcwoqIGBjbHVzdGVyYDogQSBmYWN0b3IgY29sdW1uIHdpdGggdGhlIGNsdXN0ZXIgaWRlbnRpdGllcwoqIFRoZXJlIHdpbGwgYmUgb25lIGNvbHVtbiBmb3IgZWFjaCBjbHVzdGVyaW5nIHBhcmFtZXRlciB1c2VkCgpUbyBkZW1vbnN0cmF0ZSB0aGlzIGZ1bmN0aW9uLCB3ZSdsbCBjYWxjdWxhdGUgY2x1c3RlcnMgdXNpbmcgdGhlIExvdXZhaW4gYWxnb3JpdGhtIHdoaWxlIHZhcnlpbmcgdGhlIGBubmAgcGFyYW1ldGVyOgoKYGBge3Igc3dlZXAgY2x1c3RlcnN9CiMgRGVmaW5lIG5uIHBhcmFtZXRlciB2YWx1ZXMgb2YgaW50ZXJlc3QKbm5fdmFsdWVzIDwtIGMoMTAsIDIwLCAzMCkKCiMgQ2FsY3VsYXRlIGNsdXN0ZXJzIHZhcnlpbmcgbm4sIGJ1dCBsZWF2aW5nIG90aGVyIHBhcmFtZXRlcnMgYXQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMKY2x1c3Rlcl9yZXN1bHRzX2xpc3QgPC0gck9wZW5TY1BDQTo6c3dlZXBfY2x1c3RlcnMoCiAgcGNhX21hdHJpeCwKICBubiA9IG5uX3ZhbHVlcwopCmBgYAoKVGhlIHJlc3VsdGluZyBsaXN0IGhhcyBhIGxlbmd0aCBvZiB0aHJlZSwgb25lIGRhdGEgZnJhbWUgZm9yIGVhY2ggYG5uYCBwYXJhbWV0ZXIgdGVzdGVkOgoKYGBge3IgbGVuZ3RoIGNsdXN0ZXJfcmVzdWx0c19saXN0fQpsZW5ndGgoY2x1c3Rlcl9yZXN1bHRzX2xpc3QpCmBgYAoKSXQgY2FuIGJlIGhlbHBmdWwgKGFsdGhvdWdoIGl0IGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkgdG8ga2VlcCB0cmFjaykgdG8gbmFtZSB0aGlzIGxpc3QgYnkgdGhlIHZhcmllZCBgbm5gIHBhcmFtZXRlci4KSW4gdGhpcyBjYXNlLCB3ZSdsbCB1c2UgdGhlc2UgbmFtZXMgdG8gbGFiZWwgcGxvdHMuCgpgYGB7ciBzZXQgbGlzdCBuYW1lc30KbmFtZXMoY2x1c3Rlcl9yZXN1bHRzX2xpc3QpIDwtIGdsdWU6OmdsdWUoIm5uX3tubl92YWx1ZXN9IikKYGBgCgoKV2UgY2FuIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIG9mIGVhY2ggZGF0YSBmcmFtZSB1c2luZyBbYHB1cnJyOjptYXAoKWBdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbWFwLmh0bWwpIHRvIGl0ZXJhdGUgb3ZlciB0aGUgbGlzdDoKCgpgYGB7ciBtYXAgY2x1c3Rlcl9yZXN1bHRzX2xpc3R9CmNsdXN0ZXJfcmVzdWx0c19saXN0IHw+CiAgcHVycnI6Om1hcChoZWFkKQpgYGAKCkdlbmVyYWxseSBzcGVha2luZywgYHB1cnJyOjptYXAoKWAgY2FuIGJlIHVzZWQgdG8gaXRlcmF0ZSBvdmVyIHRoaXMgbGlzdCB0byB2aXN1YWxpemUgb3IgYW5hbHl6ZSBlYWNoIGNsdXN0ZXJpbmcgcmVzdWx0IG9uIGl0cyBvd247IHdlJ2xsIHVzZSB0aGlzIGFwcHJvYWNoIGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMuIAoKIyMjIFZpc3VhbGl6aW5nIGNsdXN0ZXJpbmcgcmVzdWx0cwoKV2hlbiBjb21wYXJpbmcgY2x1c3RlcmluZyByZXN1bHRzLCBpdCdzIGltcG9ydGFudCB0byBmaXJzdCB2aXN1YWxpemUgdGhlIGRpZmZlcmVudCBjbHVzdGVyaW5ncyB0byBidWlsZCBjb250ZXh0IGZvciBpbnRlcnByZXRpbmcgUUMgbWV0cmljcy4KCkFzIG9uZSBleGFtcGxlIG9mIHdoeSB0aGlzIGlzIGltcG9ydGFudCwgd2UgZ2VuZXJhbGx5IGV4cGVjdCB0aGF0IG1vcmUgcm9idXN0IGNsdXN0ZXJzIHdpbGwgaGF2ZSBoaWdoZXIgdmFsdWVzIGZvciBtZXRyaWNzIGxpa2Ugc2lsaG91ZXR0ZSB3aWR0aCBhbmQgbmVpZ2hib3Job29kIHB1cml0eS4KSG93ZXZlciwgd2UgYWxzbyBleHBlY3QgdGhhdCBoYXZpbmcgZmV3ZXIgY2x1c3RlcnMgaW4gdGhlIGZpcnN0IHBsYWNlIHdpbGwgYWxzbyBsZWFkIHRvIGhpZ2hlciBtZXRyaWNzLCByZWdhcmRsZXNzIG9mIGNsdXN0ZXIgcXVhbGl0eTogV2hlbiB0aGVyZSBhcmUgZmV3ZXIgY2x1c3RlcnMsIGl0IGlzIG1vcmUgbGlrZWx5IHRoYXQgY2x1c3RlcnMgb3ZlcmxhcCBsZXNzIHdpdGggb25lIGFub3RoZXIganVzdCBiZWNhdXNlIHRoZXJlIGFyZW4ndCBtYW55IGNsdXN0ZXJzIGluIHRoZSBmaXJzdCBwbGFjZS4KVGhpcyBtZWFucyB0aGF0LCB3aGVuIGludGVycHJldGluZyBjbHVzdGVyIHF1YWxpdHkgbWV0cmljcywgeW91IHNob3VsZCBiZSBjYXJlZnVsIHRvIHRha2UgbW9yZSBjb250ZXh0IGFib3V0IHRoZSBkYXRhIGludG8gY29uc2lkZXJhdGlvbiBhbmQgbm90IG9ubHkgcmVseSBvbiB0aGUgbWV0cmljIHZhbHVlcy4KCldlJ2xsIHRoZXJlZm9yZSB2aXN1YWxpemUgdGhlc2UgcmVzdWx0cyBhcyBVTUFQcyBieSBpdGVyYXRpbmcgb3ZlciBgY2x1c3Rlcl9yZXN1bHRzX2xpc3RgIGFuZCBjb21iaW5pbmcgcGxvdHMgd2l0aCBbYHBhdGNod29yazo6d3JhcF9wbG90cygpYF0oaHR0cHM6Ly9wYXRjaHdvcmsuZGF0YS1pbWFnaW5pc3QuY29tL3JlZmVyZW5jZS93cmFwX3Bsb3RzLmh0bWwpLgpXZSdsbCBzcGVjaWZpY2FsbHkgdXNlIFtgcHVycnI6OmltYXAoKWBdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW1hcC5odG1sKSB0byBpdGVyYXRlIHNvIHRoYXQgd2UgY2FuIGFzc2lnbiB0aGlzIGxpc3QncyBuYW1lcyBhcyBwbG90IHRpdGxlcy4KCkZvciB0aGlzLCB3ZSdsbCBiZWdpbiBieSBleHRyYWN0aW5nIGEgdGFibGUgb2YgVU1BUCBjb29yZGluYXRlcyBmcm9tIG91ciBTQ0Ugb2JqZWN0LgoKYGBge3IgY3JlYXRlIHVtYXBfZGZ9CnVtYXBfZGYgPC0gcmVkdWNlZERpbShzY2UsICJVTUFQIikgfD4KICBhcy5kYXRhLmZyYW1lKCkKYGBgCgpOZXh0LCB3ZSdsbCBpdGVyYXRlIG92ZXIgYGNsdXN0ZXJfcmVzdWx0c19saXN0YCB0byBwbG90IHRoZSBVTUFQcy4KCmBgYHtyIHBsb3Qgbm4gdW1hcHMsIGZpZy53aWR0aCA9IDEyfQp1bWFwX3Bsb3RzIDwtIGNsdXN0ZXJfcmVzdWx0c19saXN0IHw+CiAgcHVycnI6OmltYXAoCiAgICBcKGNsdXN0ZXJfZGYsIGNsdXN0ZXJpbmdfbmFtZSkgewogICAgICAjIEFkZCBhIGNvbHVtbiB3aXRoIGNsdXN0ZXIgYXNzaWdubWVudHMgdG8gdW1hcF9kZgogICAgICB1bWFwX2RmX3Bsb3QgPC0gdW1hcF9kZiB8PgogICAgICAgIGRwbHlyOjptdXRhdGUoY2x1c3RlciA9IGNsdXN0ZXJfZGYkY2x1c3RlcikKCiAgICAgICMgUGxvdCB0aGUgVU1BUCwgY29sb3JlZCBieSB0aGUgbmV3IGNsdXN0ZXIgdmFyaWFibGUKICAgICAgZ2dwbG90KHVtYXBfZGZfcGxvdCwgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IGNsdXN0ZXIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwogICAgICAgIGxhYnModGl0bGUgPSBnbHVlOjpnbHVlKCJuZWFyZXN0IG5laWdoYm9yczoge2NsdXN0ZXJpbmdfbmFtZX0iKSkgKwogICAgICAgICMgV2UnbGwgYWRkIGEgY291cGxlIFVNQVAgcGxvdCBzZXR0aW5ncyBoZXJlLCBpbmNsdWRpbmcgZXF1YWwgYXhlcyBhbmQKICAgICAgICAjIHR1cm5pbmcgb2ZmIHRoZSBheGlzIHRpY2tzIGFuZCB0ZXh0IHNpbmNlIFVNQVAgY29vcmRpbmF0ZXMgYXJlIG5vdCBtZWFuaW5nZnVsCiAgICAgICAgY29vcmRfZXF1YWwoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQogICAgfQogICkKCiMgUHJpbnQgdGhlIHBsb3RzIHdpdGggcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKCkKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHVtYXBfcGxvdHMsIG5jb2wgPSAzKQpgYGAKClRoZXNlIHBsb3RzIHNob3cgdGhhdCB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGRlY3JlYXNlcyBhcyB0aGUgbmVhcmVzdCBuZWlnaGJvcnMgcGFyYW1ldGVyIGluY3JlYXNlcywgd2l0aCBiZXR3ZWVuIDktMTMgY2x1c3RlcnMuCgoKCgojIyMgRXZhbHVhdGluZyBjbHVzdGVyaW5nIHJlc3VsdHMKClRoaXMgc2VjdGlvbiB3aWxsIHVzZSBgcHVycnI6Om1hcCgpYCB0byBpdGVyYXRlIG92ZXIgZWFjaCBjbHVzdGVyaW5nIHJlc3VsdCBkYXRhIGZyYW1lIHRvIGNhbGN1bGF0ZSBzaWxob3VldHRlIHdpZHRoLCBuZWlnaGJvcmhvb2QgcHVyaXR5LCBhbmQgc3RhYmlsaXR5LCBhbmQgdGhlbiB2aXN1YWxpemUgcmVzdWx0cy4KVGhlIGdvYWwgb2YgdGhpcyBjb2RlIGlzIHRvIGlkZW50aWZ5IHdoZXRoZXIgb25lIGNsdXN0ZXJpbmcgcGFyYW1ldGVyaXphdGlvbiBwcm9kdWNlcyBtb3JlIHJlbGlhYmxlIGNsdXN0ZXJzLiAKCgojIyMjIFNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoYm9yaG9vZCBwdXJpdHkKCkJvdGggc2lsaG91ZXR0ZSB3aWR0aCBhbmQgbmVpZ2hib3Job29kIHB1cml0eSBhcmUgY2VsbC1sZXZlbCBxdWFudGl0aWVzLCBzbyB3ZSBjYW4gY2FsY3VsYXRlIHRoZW0gdG9nZXRoZXIgaW4gdGhlIHNhbWUgY2FsbCB0byBgcHVycnI6Om1hcCgpYC4KQmVsb3csIHdlJ2xsIGl0ZXJhdGUgb3ZlciBlYWNoIGRhdGEgZnJhbWUgaW4gYGNsdXN0ZXJfcmVzdWx0c19saXN0YCB0byBjYWxjdWxhdGUgdGhlc2UgcXVhbnRpdGllcy4KCmBgYHtyIGNhbGN1bGF0ZSBjZWxsIGxldmVsIG1ldHJpY3N9CmNlbGxfbWV0cmljX2xpc3QgPC0gY2x1c3Rlcl9yZXN1bHRzX2xpc3QgfD4KICBwdXJycjo6bWFwKAogICAgXChjbHVzdGVyX2RmKSB7CiAgICAgICMgY2FsY3VsYXRlIHNpbGhvdWV0dGUgd2lkdGgKICAgICAgc2lsaG91ZXR0ZV9kZiA8LSByT3BlblNjUENBOjpjYWxjdWxhdGVfc2lsaG91ZXR0ZShwY2FfbWF0cml4LCBjbHVzdGVyX2RmKQoKICAgICAgIyBjYWxjdWxhdGUgbmVpZ2hiaG9yaG9vZCBwdXJpdHksIHN0YXJ0aW5nIGZyb20gc2lsaG91ZXR0ZV9kZgogICAgICByT3BlblNjUENBOjpjYWxjdWxhdGVfcHVyaXR5KHBjYV9tYXRyaXgsIHNpbGhvdWV0dGVfZGYpCiAgICB9CiAgKQoKIyBWaWV3IHRoZSBmaXJzdCBzaXggcm93cyBvZiBlYWNoIGNsdXN0ZXJpbmcgcmVzdWx0J3MgY2VsbC1sZXZlbCBRQyBtZXRyaWNzCnB1cnJyOjptYXAoY2VsbF9tZXRyaWNfbGlzdCwgaGVhZCkKYGBgCgoKVG8gdmlzdWFsaXplIHRoZXNlIHJlc3VsdHMsIHdlIGNhbiBjb21iaW5lIGFsbCBkYXRhIGZyYW1lcyBpbiB0aGlzIGxpc3QgaW50byBhIHNpbmdsZSBvdmVyYWxsIGRhdGEgZnJhbWUsIHdoZXJlIHRoZSBleGlzdGluZyBgbm5gIGNvbHVtbiB3aWxsIGRpc3Rpbmd1aXNoIGFtb25nIGNvbmRpdGlvbnMuCgpgYGB7ciBjb21iaW5lIGNlbGwgbWV0cmljcyBsaXN0fQpjZWxsX21ldHJpY3NfZGYgPC0gcHVycnI6Omxpc3RfcmJpbmQoY2VsbF9tZXRyaWNfbGlzdCkKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoYm9yaG9vZCBwdXJpdHkgZWFjaCB3aXRoIGJveHBsb3RzLCBmb3IgZXhhbXBsZSwgYW5kIHVzZSB0aGUgW2BwYXRjaHdvcmtgXShodHRwczovL3BhdGNod29yay5kYXRhLWltYWdpbmlzdC5jb20vKSBwYWNrYWdlIHRvIHByaW50IHRoZW0gdG9nZXRoZXI6CgoKYGBge3J9CiMgUGxvdCBzaWxob3VldHRlIHdpZHRoCnNpbGhvdWV0dGVfcGxvdCA8LSBnZ3Bsb3QoY2VsbF9tZXRyaWNzX2RmKSArCiAgYWVzKHggPSBhcy5mYWN0b3Iobm4pLCB5ID0gc2lsaG91ZXR0ZV93aWR0aCwgZmlsbCA9IGFzLmZhY3RvcihubikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwyIikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyIsCiAgICB5ID0gIlNpbGhvdWV0dGUgd2lkdGgiCiAgKQoKCiMgUGxvdCBuZWlnaGJvcmhvb2QgcHVyaXR5IHdpZHRoCnB1cml0eV9wbG90IDwtIGdncGxvdChjZWxsX21ldHJpY3NfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBwdXJpdHksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMiLAogICAgeSA9ICJOZWlnaGJvcmhvb2QgcHVyaXR5IgogICkKCgojIEFkZCB0b2dldGhlciB1c2luZyB0aGUgcGF0Y2h3b3JrIGxpYnJhcnksIHdpdGhvdXQgYSBsZWdlbmQKc2lsaG91ZXR0ZV9wbG90ICsgcHVyaXR5X3Bsb3QgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKV2hpbGUgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGEgc2FsaWVudCBkaWZmZXJlbmNlIGFtb25nIHNpbGhvdWV0dGUgd2lkdGggZGlzdHJpYnV0aW9ucywgaXQgZG9lcyBhcHBlYXIgdGhhdCBwdXJpdHkgaXMgaGlnaGVyIHdpdGggYSBoaWdoZXIgbmVhcmVzdCBuZWlnaGJvcnMgcGFyYW1ldGVyLgpJdCdzIHdvcnRoIG5vdGluZyB0aGF0IHRoaXMgdHJlbmQgaW4gcHVyaXR5IHZhbHVlcyBpcyBleHBlY3RlZDogSGlnaGVyIG5lYXJlc3QgbmVpZ2hib3IgcGFyYW1ldGVyIHZhbHVlcyBsZWFkIHRvIGZld2VyIGNsdXN0ZXJzLCBhbmQgbmVpZ2hib3Job29kIHB1cml0eSB0ZW5kcyB0byBiZSBoaWdoZXIgd2hlbiB0aGVyZSBhcmUgZmV3ZXIgY2x1c3RlcnMuIAoKCiMjIyMgU3RhYmlsaXR5CgpOZXh0LCB3ZSdsbCBjYWxjdWxhdGUgc3RhYmlsaXR5IG9uIHRoZSBjbHVzdGVycyB1c2luZyBgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3N0YWJpbGl0eSgpYCwgc3BlY2lmeWluZyB0aGUgc2FtZSBwYXJhbWV0ZXIgdXNlZCBmb3IgdGhlIG9yaWdpbmFsIGNsdXN0ZXIgY2FsY3VsYXRpb24gYXQgZWFjaCBpdGVyYXRpb24uIAoKYGBge3IgY2FsY3VsYXRlIHN0YWJpbGl0eX0Kc3RhYmlsaXR5X2xpc3QgPC0gY2x1c3Rlcl9yZXN1bHRzX2xpc3QgfD4KICBwdXJycjo6bWFwKAogICAgXChjbHVzdGVyX2RmKSB7CiAgICAgIG5uIDwtIGNsdXN0ZXJfZGYkbm5bMV0gIyBhbGwgcm93cyBoYXZlIHRoZSBzYW1lIGBubmAgcGFyYW1ldGVyLCBzbyB3ZSdsbCB0YWtlIHRoZSBmaXJzdAoKICAgICAgIyBjYWxjdWxhdGUgc3RhYmlsaXR5LCBwYXNzaW5nIGluIHRoZSBwYXJhbWV0ZXIgdmFsdWUgdXNlZCBmb3IgdGhpcyBpdGVyYXRpb24KICAgICAgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3N0YWJpbGl0eShwY2FfbWF0cml4LCBjbHVzdGVyX2RmLCBubiA9IG5uKQogICAgfQogICkKYGBgCgpXZSdsbCBhZ2FpbiBjb21iaW5lIHRoZSBvdXRwdXQgb2YgYHN0YWJpbGl0eV9saXN0YCBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgYW5kIHBsb3QgYGFyaWAgdmFsdWVzIGFjcm9zcyBgbm5gIHBhcmFtZXRlcml6YXRpb25zLgoKCmBgYHtyIGNvbWJpbmUgcGxvdCBzdGFiaWxpdHl9CnN0YWJpbGl0eV9kZiA8LSBwdXJycjo6bGlzdF9yYmluZChzdGFiaWxpdHlfbGlzdCkKCmdncGxvdChzdGFiaWxpdHlfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBhcmksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMiLAogICAgeSA9ICJBZGp1c3RlZCBSYW5kIEluZGV4IgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpIZXJlLCB3ZSBzZWUgdGhhdCBhIG5lYXJlc3QgbmVpZ2hib3JzIHZhbHVlIG9mIDIwIG9yIDMwIGxlYWRzIHRvIG1vcmUgc3RhYmxlIGNsdXN0ZXJpbmcgcmVzdWx0cyBjb21wYXJlZCB0byAxMC4gCgoKIyMgVmFyeWluZyBtdWx0aXBsZSBjbHVzdGVyaW5nIHBhcmFtZXRlcnMKClRoZSBwcmV2aW91cyBzZWN0aW9uIGRlbW9uc3RyYXRlZCBob3cgdG8gY2FsY3VsYXRlIGNsdXN0ZXJzIGFuZCBRQyBtZXRyaWNzIHdoZW4gdmFyeWluZyBvbmUgcGFyYW1ldGVyLCBidXQgaXQgaXMgcG9zc2libGUgdG8gdmFyeSBtdWx0aXBsZSBwYXJhbWV0ZXJzIGF0IG9uY2Ugd2l0aCBgck9wZW5TY1BDQTo6c3dlZXBfY2x1c3RlcnMoKWAuCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgc2hvdyBhbiBvdmVydmlldyBvZiBob3cgeW91IG1pZ2h0IHdyaXRlIGNvZGUgdG8gdmFyeSB0d28gcGFyYW1ldGVycyBhdCBvbmNlIChoZXJlLCBuZWFyZXN0IG5laWdoYm9ycyBhbmQgcmVzb2x1dGlvbiBhcyBleGFtcGxlcykgYW5kIHZpc3VhbGl6ZSByZXN1bHRzLgoKCmBgYHtyIHN3ZWVwIHR3byBwYXJhbWV0ZXJzfQojIERlZmluZSB2ZWN0b3JzIG9mIHBhcmFtZXRlcnMgdG8gdmFyeQpubl92YWx1ZXMgPC0gYygxMCwgMjAsIDMwKQpyZXNfdmFsdWVzIDwtIGMoMC41LCAxLjAsIDEuNSkKCgpjbHVzdGVyX3Jlc3VsdHNfbGlzdCA8LSByT3BlblNjUENBOjpzd2VlcF9jbHVzdGVycygKICBwY2FfbWF0cml4LAogIG5uID0gbm5fdmFsdWVzLAogIHJlc29sdXRpb24gPSByZXNfdmFsdWVzCikKYGBgCgpUaGlzIHJlc3VsdGluZyBsaXN0IG5vdyBoYXMgOSBkaWZmZXJlbnQgY2x1c3RlcmluZyByZXN1bHRzLCBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZiBgbm5fdmFsdWVzYCBhbmQgYHJlc192YWx1ZXNgOgoKCmBgYHtyIGxlbmd0aCBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB0d28gcGFyYW1ldGVyc30KbGVuZ3RoKGNsdXN0ZXJfcmVzdWx0c19saXN0KQpgYGAKCgojIyMgVmlzdWFsaXplIGNsdXN0ZXJzCgpOZXh0LCB3ZSdsbCBpdGVyYXRlIG92ZXIgYGNsdXN0ZXJfcmVzdWx0c19saXN0YCB0byBwbG90IHRoZSBVTUFQcy4KVGhpcyB0aW1lLCB3ZSdsbCB1c2UgYHB1cnJyOjptYXAoKWAgYW5kIHB1bGwgb3V0IHBhcmFtZXRlcnMgZnJvbSBlYWNoIGl0ZXJhdGlvbidzIGBjbHVzdGVyX2RmYCB0byBmb3JtIHRoZSBVTUFQIHBhbmVsIHRpdGxlLgoKYGBge3IgcGxvdCBubiByZXMgdW1hcHMsIGZpZy5oZWlnaHQgPSAxNH0KdW1hcF9wbG90cyA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgIyBBZGQgYSBjb2x1bW4gd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIHRvIHVtYXBfZGYKICAgICAgdW1hcF9kZl9wbG90IDwtIHVtYXBfZGYgfD4KICAgICAgICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyX2RmJGNsdXN0ZXIpCgogICAgICAjIENyZWF0ZSBhIHRpdGxlIGZvciB0aGUgVU1BUCB3aXRoIGJvdGggcGFyYW1ldGVycwogICAgICB1bWFwX3RpdGxlIDwtIGdsdWU6OmdsdWUoCiAgICAgICAgIm5uOiB7Y2x1c3Rlcl9kZiRublsxXX07IHJlczoge2NsdXN0ZXJfZGYkcmVzb2x1dGlvblsxXX0iCiAgICAgICkKCiAgICAgICMgUGxvdCB0aGUgVU1BUCwgY29sb3JlZCBieSB0aGUgbmV3IGNsdXN0ZXIgdmFyaWFibGUKICAgICAgZ2dwbG90KHVtYXBfZGZfcGxvdCwgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IGNsdXN0ZXIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwogICAgICAgIGxhYnModGl0bGUgPSB1bWFwX3RpdGxlKSArCiAgICAgICAgIyBXZSdsbCBhZGQgYSBjb3VwbGUgVU1BUC1zcGVjaWZpYyBwbG90IHNldHRpbmdzCiAgICAgICAgY29vcmRfZXF1YWwoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgIyBFbnN1cmUgbGVnZW5kcyBmaXQgaW4gdGhlIGZpZ3VyZQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMiwgImNtIikKICAgICAgICApCiAgICB9CiAgKQoKIyBQcmludCB0aGUgcGxvdHMgd2l0aCBwYXRjaHdvcms6OndyYXBfcGxvdHMoKQpwYXRjaHdvcms6OndyYXBfcGxvdHModW1hcF9wbG90cywgbmNvbCA9IDMpCmBgYAoKCgojIyMgQ2FsY3VsYXRlIGFuZCB2aXN1YWxpemUgUUMgbWV0cmljcwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIG9uZSBjb2Rpbmcgc3RyYXRlZ3kgdG8gY2FsY3VsYXRlIGFuZCB2aXN1YWxpemUgcmVzdWx0cyB3aGVuIHZhcnlpbmcgdHdvIGNsdXN0ZXJpbmcgcGFyYW1ldGVycy4KSW4gcGFydGljdWxhciwgd2UgdXNlIGZhY2V0aW5nIHRvIGhlbHAgZGlzcGxheSBhbGwgaW5mb3JtYXRpb24gaW4gb25lIHBsb3QsIGJ5IHBsYWNpbmcgbmVhcmVzdCBuZWlnaGJvciB2YWx1ZXMgb24gdGhlIFgtYXhpcyBhbmQgZmFjZXRpbmcgYnkgcmVzb2x1dGlvbiB2YWx1ZXMuClNpbmNlIHNpbGhvdWV0dGUgd2lkdGggYW5kIG5laWdoaG9yYm9vZCBwdXJpdHkgY2FsY3VsYXRpb25zIHVzaW5nIGdlbmVyYWxseSBzaW1pbGFyIGNvZGUsIHdlJ2xsIGp1c3Qgc2hvdyBuZWlnaGJvcmhvb2QgcHVyaXR5IGhlcmUuCgojIyMjIE5laWdoYm9yaG9vZCBwdXJpdHkKCkZpcnN0LCB3ZSdsbCBjYWxjdWxhdGUgbmVpZ2hib3Job29kIHB1cml0eSBhbmQgY29tYmluZSByZXN1bHRzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZS4KCmBgYHtyIGNhbGN1bGF0ZSBwdXJpdHkgdHdvIHBhcmFtZXRlcnN9CnB1cml0eV9kZiA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgck9wZW5TY1BDQTo6Y2FsY3VsYXRlX3B1cml0eShwY2FfbWF0cml4LCBjbHVzdGVyX2RmKQogICAgfQogICkgfD4KICBwdXJycjo6bGlzdF9yYmluZCgpCmBgYAoKCmBgYHtyIHZpc3VhbGl6ZSBwdXJpdHkgdHdvIHBhcmFtZXRlcnN9CmdncGxvdChwdXJpdHlfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBwdXJpdHksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICAjIGZhY2V0IGJ5IHJlc29sdXRpb24sIGxhYmVsaW5nIHBhbmVscyB3aXRoIGJvdGggdGhlIHJlc29sdXRpb24gY29sdW1uIG5hbWUgYW5kIHZhbHVlCiAgZmFjZXRfd3JhcCh2YXJzKHJlc29sdXRpb24pLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMiLAogICAgeSA9ICJOZWlnaGJvcmhvb2QgcHVyaXR5IgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyMgU3RhYmlsaXR5CgpTaW1pbGFyIHRvIGFib3ZlLCB3ZSdsbCBjYWxjdWxhdGUgc3RhYmlsaXR5LCBjb21iaW5lIHJlc3VsdHMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lLCBhZGQgYSBgcmVzb2x1dGlvbl9sYWJlbGAgY29sdW1uIHRvIHN1cHBvcnQgcGxvdCBpbnRlcnByZXRhdGlvbiwgYW5kIGZpbmFsbHkgbWFrZSBvdXIgcGxvdC4KCmBgYHtyIGNhbGN1bGF0ZSBzdGFiaWxpdHkgdHdvIHBhcmFtZXRlcnN9CnN0YWJpbGl0eV9kZiA8LSBjbHVzdGVyX3Jlc3VsdHNfbGlzdCB8PgogIHB1cnJyOjptYXAoCiAgICBcKGNsdXN0ZXJfZGYpIHsKICAgICAgIyBFeHRyYWN0IHBhcmFtZXRlcnMgZm9yIHRoaXMgY2x1c3RlcmluZyByZXN1bHQKICAgICAgbm4gPC0gdW5pcXVlKGNsdXN0ZXJfZGYkbm4pCiAgICAgIHJlc29sdXRpb24gPC0gdW5pcXVlKGNsdXN0ZXJfZGYkcmVzb2x1dGlvbikKCiAgICAgIHJPcGVuU2NQQ0E6OmNhbGN1bGF0ZV9zdGFiaWxpdHkoCiAgICAgICAgcGNhX21hdHJpeCwKICAgICAgICBjbHVzdGVyX2RmLAogICAgICAgIG5uID0gbm4sCiAgICAgICAgcmVzb2x1dGlvbiA9IHJlc29sdXRpb24KICAgICAgKQogICAgfQogICkgfD4KICBwdXJycjo6bGlzdF9yYmluZCgpCmBgYAoKCmBgYHtyIHZpc3VhbGl6ZSBzdGFiaWxpdHkgdHdvIHBhcmFtZXRlcnN9CmdncGxvdChzdGFiaWxpdHlfZGYpICsKICBhZXMoeCA9IGFzLmZhY3RvcihubiksIHkgPSBhcmksIGZpbGwgPSBhcy5mYWN0b3Iobm4pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICBmYWNldF93cmFwKHZhcnMocmVzb2x1dGlvbiksIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyIsCiAgICB5ID0gIkFkanVzdGVkIFJhbmQgSW5kZXgiCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7ciBzZXNzaW9uIGluZm99CiMgcmVjb3JkIHRoZSB2ZXJzaW9ucyBvZiB0aGUgcGFja2FnZXMgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGFuZCBvdGhlciBlbnZpcm9ubWVudCBpbmZvcm1hdGlvbgpzZXNzaW9uSW5mbygpCmBgYAo=