From 30b8015debc6de793cf7d8c38f039d3c3834bcc0 Mon Sep 17 00:00:00 2001 From: sjspielman <4701111+sjspielman@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:26:54 +0000 Subject: [PATCH 1/2] Live and rendered notebooks --- RNA-seq/02-gastric_cancer_tximeta-live.Rmd | 16 +- RNA-seq/02-gastric_cancer_tximeta.nb.html | 76 +- .../03-gastric_cancer_exploratory-live.Rmd | 20 +- RNA-seq/03-gastric_cancer_exploratory.nb.html | 86 +- RNA-seq/04-nb_cell_line_tximeta.nb.html | 4 +- RNA-seq/05-nb_cell_line_DESeq2-live.Rmd | 54 +- RNA-seq/05-nb_cell_line_DESeq2.nb.html | 99 +- RNA-seq/06-openpbta_heatmap-live.Rmd | 6 +- RNA-seq/06-openpbta_heatmap.nb.html | 339 +- .../01-intro_to_base_R-live.Rmd | 2 +- .../01-intro_to_base_R.nb.html | 14 +- .../02-intro_to_ggplot2-live.Rmd | 2 +- .../02-intro_to_ggplot2.nb.html | 14 +- .../03-intro_to_tidyverse-live.Rmd | 4 +- .../03-intro_to_tidyverse.nb.html | 16 +- .../01-overrepresentation_analysis-live.Rmd | 62 +- .../01-overrepresentation_analysis.nb.html | 3459 +++++++++++++-- .../02-gene_set_enrichment_analysis-live.Rmd | 37 +- .../02-gene_set_enrichment_analysis.nb.html | 199 +- .../03-gene_set_variation_analysis-live.Rmd | 104 +- .../03-gene_set_variation_analysis.nb.html | 3760 +++++++++++++---- .../01-read_filter_normalize_scRNA.nb.html | 14 +- .../02-dataset_integration-live.Rmd | 2 +- .../02-dataset_integration.nb.html | 22 +- .../03-differential_expression.nb.html | 14 +- .../04-overrepresentation_analysis.nb.html | 14 +- .../05-gene_set_enrichment_analysis.nb.html | 18 +- scRNA-seq/01-scRNA_quant_qc.nb.html | 6 +- scRNA-seq/02-filtering_scRNA.nb.html | 31 +- scRNA-seq/03-normalizing_scRNA.nb.html | 12 +- .../04-dimension_reduction_scRNA.nb.html | 12 +- .../05-clustering_markers_scRNA-live.Rmd | 2 +- scRNA-seq/05-clustering_markers_scRNA.nb.html | 16 +- scRNA-seq/06-celltype_annotation.nb.html | 10 +- 34 files changed, 6835 insertions(+), 1711 deletions(-) diff --git a/RNA-seq/02-gastric_cancer_tximeta-live.Rmd b/RNA-seq/02-gastric_cancer_tximeta-live.Rmd index 645c251d..e9a8bec1 100644 --- a/RNA-seq/02-gastric_cancer_tximeta-live.Rmd +++ b/RNA-seq/02-gastric_cancer_tximeta-live.Rmd @@ -53,8 +53,12 @@ We'll need the `quant.sf` files for all the samples in an experiment which we ha ```{r input-names} # the quant files themselves -sf_files <- list.files(quant_dir, recursive = TRUE, full.names = TRUE, - pattern = "quant.sf") +sf_files <- list.files( + quant_dir, + recursive = TRUE, + full.names = TRUE, + pattern = "quant.sf" +) ``` ```{r metadata-file} @@ -65,7 +69,7 @@ meta_file <- file.path(data_dir, "gastric-cancer_metadata.tsv") **Output** ```{r output-names, live = TRUE} -# Name the output gastric-cancer_tximeta.RDS and use the directory created +# Name the output gastric-cancer_tximeta.rds and use the directory created # above as the rest of the path ``` @@ -98,8 +102,10 @@ sample_names - a `names` column with the sample names ```{r names_sf_files} -coldata <- data.frame(files = sf_files, - names = sample_names) +coldata <- data.frame( + files = sf_files, + names = sample_names +) ``` We have more information about these samples stored in the metadata file that we will also want stored in `coldata`. diff --git a/RNA-seq/02-gastric_cancer_tximeta.nb.html b/RNA-seq/02-gastric_cancer_tximeta.nb.html index 381101a7..5ad82e07 100644 --- a/RNA-seq/02-gastric_cancer_tximeta.nb.html +++ b/RNA-seq/02-gastric_cancer_tximeta.nb.html @@ -3040,7 +3040,7 @@
# directory where the data are located
data_dir <- file.path("data", "gastric-cancer")
@@ -3050,9 +3050,7 @@ Directories and files
# create a directory to hold the tximeta results if it doesn't exist yet
txi_dir <- file.path(data_dir, "txi")
-if (!dir.exists(txi_dir)) {
- dir.create(txi_dir, recursive = TRUE)
-}
+fs::dir_create(txi_dir)
@@ -3060,10 +3058,14 @@ quant_dir
.
-
+
# the quant files themselves
-sf_files <- list.files(quant_dir, recursive = TRUE, full.names = TRUE,
- pattern = "quant.sf")
+sf_files <- list.files(
+ quant_dir,
+ recursive = TRUE,
+ full.names = TRUE,
+ pattern = "quant.sf"
+)
@@ -3078,10 +3080,10 @@ Output
- -# Name the output gastric-cancer_tximeta.RDS and use the directory created
+
+# Name the output gastric-cancer_tximeta.rds and use the directory created
# above as the rest of the path
-txi_out_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS")
+txi_out_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds")
@@ -3158,9 +3160,11 @@ names
column with the sample names
-
-coldata <- data.frame(files = sf_files,
- names = sample_names)
+
+coldata <- data.frame(
+ files = sf_files,
+ names = sample_names
+)
@@ -3260,8 +3264,8 @@ # Summarize to the gene level
gene_summarized <- summarizeToGene(txi_data)
-
-loading existing EnsDb created: 2024-05-29 19:53:16
+
+loading existing EnsDb created: 2024-08-08 16:10:27
obtaining transcript-to-gene mapping from database
@@ -3436,14 +3440,14 @@ # Let's look at the first few rows of the gene-level TPM
head(assay(gene_summarized, "abundance"))
-
+
SRR585570 SRR585571 SRR585572 SRR585573 SRR585574 SRR585575
-ENSG00000000003 25.016289 18.896831 12.288181 26.911045 22.088410 17.168736
-ENSG00000000005 0.121647 0.000000 0.000000 0.000000 0.000000 0.000000
-ENSG00000000419 26.671423 20.771196 103.246348 69.495297 66.335181 77.471536
-ENSG00000000457 5.657513 2.921236 6.511128 5.107480 5.106009 3.845323
-ENSG00000000460 1.757068 2.933740 1.354462 2.195826 6.341131 16.792151
-ENSG00000000938 1.692824 2.807437 0.078625 0.000000 0.028502 0.000000
+ENSG00000000003 25.014898 18.896831 12.288181 26.911045 22.088410 17.168736
+ENSG00000000005 0.121627 0.000000 0.000000 0.000000 0.000000 0.000000
+ENSG00000000419 26.667591 20.771196 103.246348 69.495297 66.335181 77.471536
+ENSG00000000457 5.653614 2.921236 6.511128 5.107480 5.106009 3.845323
+ENSG00000000460 1.757371 2.933740 1.354462 2.195826 6.341131 16.792151
+ENSG00000000938 1.693652 2.807437 0.078625 0.000000 0.028502 0.000000
SRR585576 SRR585577
ENSG00000000003 17.974009 29.513266
ENSG00000000005 0.000000 0.000000
@@ -3481,8 +3485,8 @@ Session Info
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3518,7 +3522,7 @@ Session Info
loaded via a namespace (and not attached):
[1] DBI_1.2.2 bitops_1.0-7 httr2_1.0.1
[4] biomaRt_2.60.0 rlang_1.1.3 magrittr_2.0.3
- [7] compiler_4.4.0 RSQLite_2.3.6 png_0.1-8
+ [7] compiler_4.4.1 RSQLite_2.3.6 png_0.1-8
[10] vctrs_0.6.5 txdbmaker_1.0.0 stringr_1.5.1
[13] ProtGenerics_1.36.0 pkgconfig_2.0.3 crayon_1.5.2
[16] fastmap_1.1.1 dbplyr_2.5.0 XVector_0.44.0
@@ -3527,7 +3531,7 @@ Session Info
[25] bit_4.0.5 xfun_0.43 zlibbioc_1.50.0
[28] cachem_1.0.8 jsonlite_1.8.8 progress_1.2.3
[31] blob_1.2.4 DelayedArray_0.30.0 BiocParallel_1.38.0
-[34] parallel_4.4.0 prettyunits_1.2.0 R6_2.5.1
+[34] parallel_4.4.1 prettyunits_1.2.0 R6_2.5.1
[37] bslib_0.7.0 stringi_1.8.3 rtracklayer_1.64.0
[40] jquerylib_0.1.4 knitr_1.46 readr_2.1.5
[43] Matrix_1.7-0 tidyselect_1.2.1 abind_1.4-5
@@ -3538,20 +3542,20 @@ Session Info
[58] pillar_1.9.0 BiocManager_1.30.22 filelock_1.0.3
[61] generics_0.1.3 vroom_1.6.5 RCurl_1.98-1.14
[64] BiocVersion_3.19.1 hms_1.1.3 glue_1.7.0
-[67] lazyeval_0.2.2 tools_4.4.0 AnnotationHub_3.12.0
-[70] BiocIO_1.14.0 GenomicAlignments_1.40.0 XML_3.99-0.16.1
-[73] grid_4.4.0 GenomeInfoDbData_1.2.12 restfulr_0.0.15
-[76] cli_3.6.2 rappdirs_0.3.3 fansi_1.0.6
-[79] S4Arrays_1.4.0 dplyr_1.1.4 sass_0.4.9
-[82] digest_0.6.35 SparseArray_1.4.0 tximport_1.32.0
-[85] rjson_0.2.21 memoise_2.0.1 htmltools_0.5.8.1
-[88] lifecycle_1.0.4 httr_1.4.7 mime_0.12
-[91] bit64_4.0.5
+[67] lazyeval_0.2.2 tools_4.4.1 AnnotationHub_3.12.0
+[70] BiocIO_1.14.0 GenomicAlignments_1.40.0 fs_1.6.4
+[73] XML_3.99-0.16.1 grid_4.4.1 GenomeInfoDbData_1.2.12
+[76] restfulr_0.0.15 cli_3.6.2 rappdirs_0.3.3
+[79] fansi_1.0.6 S4Arrays_1.4.0 dplyr_1.1.4
+[82] sass_0.4.9 digest_0.6.35 SparseArray_1.4.0
+[85] tximport_1.32.0 rjson_0.2.21 memoise_2.0.1
+[88] htmltools_0.5.8.1 lifecycle_1.0.4 httr_1.4.7
+[91] mime_0.12 bit64_4.0.5
-LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGdlbmUtbGV2ZWwgc3VtbWFyaXphdGlvbiB3aXRoIGB0eGltZXRhYCIKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIEltcG9ydCBSTkEtc2VxIGV4cHJlc3Npb24gcXVhbnRpZmljYXRpb24gb3V0cHV0IHVzaW5nIGB0eGltZXRhYAotIFN1bW1hcml6ZSB0cmFuc2NyaXB0LWxldmVsIGV4cHJlc3Npb24gdG8gdGhlIGdlbmUgbGV2ZWwKLSBJbnRlcnJvZ2F0ZSBhbmQgZXh0cmFjdCBkYXRhIGZyb20gYSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdAoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBpbXBvcnQgdGhlIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiBxdWFudGlmaWNhdGlvbiBvdXRwdXQgZnJvbSBgc2FsbW9uIHF1YW50YCB1c2luZyB0aGUgW2B0eGltZXRhYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3R4aW1ldGEuaHRtbCkgcGFja2FnZS4KYHR4aW1ldGFgIGlzIGluIHBhcnQgYSB3cmFwcGVyIGFyb3VuZCBhbm90aGVyIHBhY2thZ2UsIFtgdHhpbXBvcnRgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvdHhpbXBvcnQuaHRtbCksIHdoaWNoIGltcG9ydHMgdHJhbnNjcmlwdCBleHByZXNzaW9uIGRhdGEgYW5kIHN1bW1hcml6ZXMgaXQgdG8gdGhlIGdlbmUgbGV2ZWwuCldvcmtpbmcgYXQgdGhlIGdlbmUgcmF0aGVyIHRoYW4gdHJhbnNjcmlwdCBsZXZlbCBoYXMgYSBudW1iZXIgb2YgcG90ZW50aWFsIGFkdmFudGFnZXMgZm9yIGludGVycHJldGFiaWxpdHksIGVmZmljaWVuY3ksIGFuZCByZWR1Y3Rpb24gb2YgZmFsc2UgcG9zaXRpdmVzIChbU29uZXNvbiBfZXQgYWwuXyAyMDE2XShodHRwczovL2RvaS5vcmcvMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43NTYzLjIpKS4KYHR4aW1ldGFgIGVhc2VzIHNvbWUgb2YgdGhlIGJ1cmRlbiBvZiBpbXBvcnQgYnkgYXV0b21hdGljYWxseSBpZGVudGlmeWluZyB0aGUgY29ycmVjdCBzZXQgb2YgYW5ub3RhdGlvbiBkYXRhIHRvIGFwcGVuZCB0byBtYW55IGRhdGEgc2V0cyAoW0xvdmUgX2V0IGFsLl8gMjAyMF0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDA3NjY0KSkuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV81LnBuZykKCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGB0eGltZXRhYCwgc2VlIFt0aGlzIGV4Y2VsbGVudCB2aWduZXR0ZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCkgZnJvbSBMb3ZlIF9ldCBhbF8uCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeX0KIyBMb2FkIHRoZSB0eGltZXRhIHBhY2thZ2UKbGlicmFyeSh0eGltZXRhKQoKIyBMb2FkIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBwYWNrYWdlCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmBgYAoKIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpgYGB7ciBkaXJlY3RvcmllcywgbGl2ZSA9IFRSVUV9CiMgZGlyZWN0b3J5IHdoZXJlIHRoZSBkYXRhIGFyZSBsb2NhdGVkCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJnYXN0cmljLWNhbmNlciIpCgojIGRpcmVjdG9yeSB3aGVyZSB0aGUgcXVhbnQgZmlsZXMgYXJlIGxvY2F0ZWQsIGVhY2ggc2FtcGxlIGlzIGl0cyBvd24KIyBkaXJlY3RvcnkKcXVhbnRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInNhbG1vbl9xdWFudCIpCgojIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSB0eGltZXRhIHJlc3VsdHMgaWYgaXQgZG9lc24ndCBleGlzdCB5ZXQKdHhpX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJ0eGkiKQppZiAoIWRpci5leGlzdHModHhpX2RpcikpIHsKICBkaXIuY3JlYXRlKHR4aV9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgpXZSdsbCBuZWVkIHRoZSBgcXVhbnQuc2ZgIGZpbGVzIGZvciBhbGwgdGhlIHNhbXBsZXMgaW4gYW4gZXhwZXJpbWVudCB3aGljaCB3ZSBoYXZlIHN0b3JlZCBpbiBgcXVhbnRfZGlyYC4KCmBgYHtyIGlucHV0LW5hbWVzfQojIHRoZSBxdWFudCBmaWxlcyB0aGVtc2VsdmVzCnNmX2ZpbGVzIDwtIGxpc3QuZmlsZXMocXVhbnRfZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBmdWxsLm5hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gInF1YW50LnNmIikKYGBgCgpgYGB7ciBtZXRhZGF0YS1maWxlfQojIHNhbXBsZSBtZXRhZGF0YSBmaWxlCm1ldGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJnYXN0cmljLWNhbmNlcl9tZXRhZGF0YS50c3YiKQpgYGAKCioqT3V0cHV0KioKCmBgYHtyIG91dHB1dC1uYW1lcywgbGl2ZSA9IFRSVUV9CiMgTmFtZSB0aGUgb3V0cHV0IGdhc3RyaWMtY2FuY2VyX3R4aW1ldGEuUkRTIGFuZCB1c2UgdGhlIGRpcmVjdG9yeSBjcmVhdGVkCiMgYWJvdmUgYXMgdGhlIHJlc3Qgb2YgdGhlIHBhdGgKdHhpX291dF9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5SRFMiKQpgYGAKCiMjIEZpbGUgbmFtZXMKCkFsbCBvdXRwdXQgZmlsZXMgZnJvbSBgc2FsbW9uIHF1YW50YCB3ZSdsbCB1c2Ugd2l0aCBgdHhpbWV0YWAgYXJlIG5hbWVkIGBxdWFudC5zZmAuClVuZm9ydHVuYXRlbHksIHRoaXMgbWVhbnMgdGhhdCB0aGUgZmlsZSBuYW1lcyB0aGVtc2VsdmVzIGRvIG5vdCBoYXZlIGFueSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlIHRoZXkgY29tZSBmcm9tIQoKYGBge3Igc2ZfZmlsZXMsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZ1bGwgcGF0aCBmb3IgdGhlIHF1YW50LnNmIGZpbGVzCnNmX2ZpbGVzCmBgYAoKTGV0J3MgZXh0cmFjdCB0aGUgX3NhbXBsZV8gbmFtZXMgZnJvbSB0aGUgKipmaWxlIHBhdGhzKiogdXNpbmcgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLgoKTm90aWNlIGhvdyB0aGUgZmlsZSBwYXRoIGlzIHNlcGFyYXRlZCBieSBgL2AuCklmIHdlIHdlcmUgdG8gc3BsaXQgdXAgdGhpcyBjaGFyYWN0ZXIgc3RyaW5nIGJ5IGAvYCwgdGhlIHNlY29uZCB0byBsYXN0IGl0ZW0gaXMgdGhlIHNhbXBsZSBuYW1lcyAoYmVjYXVzZSB3ZSB1c2VkIHRoZW0gYXMgZGlyZWN0b3J5IG5hbWVzIGZvciB0aGUgYHNhbG1vbmAgb3V0cHV0KS4KVGhpcyBpcyBleGFjdGx5IHdoYXQgYHN0cmluZ3I6OndvcmQoKWAgYWxsb3dzIHVzIHRvIGRvOiBzcGxpdCB1cCB0aGUgZmlsZSBwYXRocyBieSBgL2AgYW5kIGV4dHJhY3QgdGhlIHNhbXBsZSBuYW1lcy4KCmBgYHtyIHNhbXBsZV9uYW1lc30Kc2FtcGxlX25hbWVzIDwtIHN0cmluZ3I6OndvcmQoc2ZfZmlsZXMsIC0yLCBzZXAgPSAiLyIpCnNhbXBsZV9uYW1lcwpgYGAKCiMjIFNldCB1cCBtZXRhZGF0YQoKYHR4aW1ldGFgIG5lZWRzIGEgZGF0YSBmcmFtZSB3aXRoIGF0IGxlYXN0IHRoZXNlIHR3byBjb2x1bW5zOgotIGEgYGZpbGVzYCBjb2x1bW4gIHdpdGggdGhlIGZpbGUgcGF0aHMgdG8gdGhlIHF1YW50LnNmIGZpbGVzCi0gYSBgbmFtZXNgIGNvbHVtbiB3aXRoIHRoZSBzYW1wbGUgbmFtZXMKCmBgYHtyIG5hbWVzX3NmX2ZpbGVzfQpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoZmlsZXMgPSBzZl9maWxlcywKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzID0gc2FtcGxlX25hbWVzKQpgYGAKCldlIGhhdmUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGVzZSBzYW1wbGVzIHN0b3JlZCBpbiB0aGUgbWV0YWRhdGEgZmlsZSB0aGF0IHdlIHdpbGwgYWxzbyB3YW50IHN0b3JlZCBpbiBgY29sZGF0YWAuCkxldCdzIHJlYWQgaW4gdGhlIHNhbXBsZSBtZXRhZGF0YSBmcm9tIHRoZSBUU1YgZmlsZS4KCmBgYHtyIHNhbXBsZV9tZXRhX2RmLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBzYW1wbGUgbWV0YWRhdGEgVFNWIGZpbGUgYW5kIGhhdmUgYSBsb29rCnNhbXBsZV9tZXRhX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihtZXRhX2ZpbGUpCnNhbXBsZV9tZXRhX2RmCmBgYAoKV2UnbGwgd2FudCB0aGlzIGluZm9ybWF0aW9uIHRvIGJlIGFkZGVkIHRvIHRoZSBgY29sZGF0YWAsIHdoaWNoIHdlIGNhbiBkbyBieSB1c2luZyBhIGpvaW4gZnVuY3Rpb24gdG8gbWF0Y2ggdXAgdGhlIHJvd3MgYmV0d2VlbiB0aGUgdHdvIGRhdGEgZnJhbWVzIGFuZCBjb21iaW5lIHRoZW0uCgpgYGB7ciBqb2luLXNhbXBsZV9tZXRhX2RmfQpjb2xkYXRhIDwtIGNvbGRhdGEgfD4KICBkcGx5cjo6aW5uZXJfam9pbihzYW1wbGVfbWV0YV9kZiwgYnkgPSBjKCJuYW1lcyIgPSAic3JyX2FjY2Vzc2lvbiIpKQoKY29sZGF0YQpgYGAKCiMjIEltcG9ydCBleHByZXNzaW9uIGRhdGEgd2l0aCBgdHhpbWV0YWAKClVzaW5nIHRoZSBgY29sZGF0YWAgZGF0YSBmcmFtZSB0aGF0IHdlIHNldCB1cCwgd2UgY2FuIG5vdyBydW4gdGhlIGB0eGltZXRhKClgIHRvIGltcG9ydCBvdXIgZXhwcmVzc2lvbiBkYXRhIHdoaWxlIGF1dG9tYXRpY2FsbHkgZmluZGluZyBhbmQgYXNzb2NpYXRpbmcgdGhlIHRyYW5zY3JpcHQgYW5ub3RhdGlvbnMgdGhhdCB3ZXJlIHVzZWQgd2hlbiB3ZSBwZXJmb3JtZWQgdGhlIHF1YW50aWZpY2F0aW9uLgoKVGhlIGZpcnN0IHRpbWUgeW91IHJ1biBgdHhpbWV0YSgpYCB5b3UgbWF5IGdldCBhIG1lc3NhZ2UgYWJvdXQgc3RvcmluZyBkb3dubG9hZGVkIHRyYW5zY3JpcHRvbWUgZGF0YSBpbiBhIGNhY2hlIGRpcmVjdG9yeSBzbyB0aGF0IGl0IGNhbiByZXRyaWV2ZSB0aGUgZGF0YSBtb3JlIHF1aWNrbHkgdGhlIG5leHQgdGltZS4KV2UgcmVjb21tZW5kIHlvdSB1c2UgdGhlIGNhY2hlLCBhbmQgYWNjZXB0IHRoZSBkZWZhdWx0IGxvY2F0aW9uLgoKYGBge3IgdHhpbWV0YSwgbGl2ZSA9IFRSVUV9CnR4aV9kYXRhIDwtIHR4aW1ldGEoY29sZGF0YSkKYGBgCgoqdHhpbWV0YSBjdXJyZW50bHkgd29ya3MgZWFzaWx5IGZvciBtb3N0IGh1bWFuIGFuZCBtb3VzZSBkYXRhc2V0cywgYnV0IHJlcXVpcmVzIGEgW2ZldyBtb3JlIHN0ZXBzIGZvciBvdGhlciBzcGVjaWVzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCNXaGF0X2lmX2NoZWNrc3VtX2lzbiVFMiU4MCU5OXRfa25vd24pLgoKIyMgU3VtbWFyaXplIHRvIGdlbmUKCldlJ2xsIHN1bW1hcml6ZSB0byB0aGUgZ2VuZSBsZXZlbCB1c2luZyB0aGUgYHN1bW1hcml6ZVRvR2VuZSgpYCBmdW5jdGlvbi4KCmBgYHtyIHN1bW1hcml6ZS1nZW5lfQojIFN1bW1hcml6ZSB0byB0aGUgZ2VuZSBsZXZlbApnZW5lX3N1bW1hcml6ZWQgPC0gc3VtbWFyaXplVG9HZW5lKHR4aV9kYXRhKQpgYGAKCldlIGNhbiB1c2UgdGhlIGBjbGFzc2AgZnVuY3Rpb24gdG8gc2VlIHdoYXQgdHlwZSBvZiBvYmplY3QgYGdlbmVfc3VtbWFyaXplZGAgaXMuCgpgYGB7ciBjbGFzcywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0eXBlIG9mIG9iamVjdCBgZ2VuZV9zdW1tYXJpemVkYCBpcwpjbGFzcyhnZW5lX3N1bW1hcml6ZWQpCmBgYAoKVGhpcyB0ZWxscyB1cyB0aGF0IGBnZW5lX3N1bW1hcml6ZWRgIGlzIGFuIG9iamVjdCBjYWxsZWQgYSBbYFN1bW1hcml6ZWRFeHBlcmltZW50YF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL1N1bW1hcml6ZWRFeHBlcmltZW50L3ZlcnNpb25zLzEuMi4zL3RvcGljcy9TdW1tYXJpemVkRXhwZXJpbWVudC1jbGFzcykgd2hpY2ggY2FuIGJlIGhhbmRsZWQgYnkgZnVuY3Rpb25zIGZyb20gdGhlIHBhY2thZ2Ugb2YgdGhlIHNhbWUgbmFtZS4KV2UgbW9yZSBzcGVjaWZpY2FsbHkgaGF2ZSBhIGBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudGAgd2hpY2ggaXMgYSBtb3JlIHNwZWNpZmljIHR5cGUgb2YgYFN1bW1hcml6ZWRFeHBlcmltZW50YC4KCmBTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0cyBoYXZlIHRoaXMgZ2VuZXJhbCBzdHJ1Y3R1cmU6CgohW1N1bW1hcml6ZWRFeHBlcmltZW50XShkaWFncmFtcy9TdW1tYXJpemVFeHBlcmltZW50LXN0cnVjdHVyZS5wbmcpCgpUaGlzIGZpZ3VyZSBpcyBmcm9tIHRoaXMgaGFuZHkgdmlnbmV0dGUgYWJvdXQgW2BTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0c10oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9TdW1tYXJpemVkRXhwZXJpbWVudC9pbnN0L2RvYy9TdW1tYXJpemVkRXhwZXJpbWVudC5odG1sKS4KCkFzIHNob3duIGluIHRoZSBkaWFncmFtLCB3ZSBjYW4gdXNlIHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyBwcm92aWRlZCBieSB0aGUgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBwYWNrYWdlIHRvIGV4dHJhY3QgZGF0YSBmcm9tIG91ciBgZ2VuZV9zdW1tYXJpemVkYCBvYmplY3QuCkZvciBleGFtcGxlLCBjYWxsaW5nIGByb3dEYXRhKClgIG9uIG91ciBvYmplY3Qgc2hvd3MgYWxsIHRoZSBnZW5lIGluZm9ybWF0aW9uIHRoYXQgYHR4aW1ldGFgIHNldCB1cCEKCmBgYHtyfQojIHJvd0RhdGEoKSBzaG93cyB1cyBvdXIgZ2VuZSBhbm5vdGF0aW9uCnJvd0RhdGEoZ2VuZV9zdW1tYXJpemVkKQpgYGAKClRoZSBgYXNzYXlgIHNsb3QgaW4gYFN1bW1hcml6ZWRFeHBlcmltZW50YHMgaG9sZHMgZGF0YSBmcm9tIHRoZSBleHBlcmltZW50LgpJbiB0aGlzIGNhc2UsIGl0IHdpbGwgaW5jbHVkZSBvdXIgZ2VuZS1sZXZlbCBleHByZXNzaW9uIGluZm9ybWF0aW9uIHN0b3JlZCBhcyBhIGdlbmUgeCBzYW1wbGUgbWF0cml4LgoKTXVsdGlwbGUgYGFzc2F5c2AgY2FuIGJlIHN0b3JlZCBpbiBhbiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIGFuZCB3ZSBjYW4gdXNlIHRoZSBgYXNzYXlOYW1lcygpYCBmdW5jdGlvbiB0byBzZWUgd2hhdCBhc3NheXMgYXJlIGluY2x1ZGVkIGluIGBnZW5lX3N1bW1hcml6ZWRgLgoKYGBge3IgYXNzYXktbmFtZXMsIGxpdmUgPSBUUlVFfQphc3NheU5hbWVzKGdlbmVfc3VtbWFyaXplZCkKYGBgCgpJZiB3ZSB3YW50IHRvIGV4dHJhY3QgYW4gYGFzc2F5YCdzIGRhdGEsIHdlIGNhbiB1c2UgYGFzc2F5KClgIGZ1bmN0aW9uIGFuZCBzcGVjaWZ5IHRoZSBuYW1lIG9mIHRoZSBhc3NheSB3ZSB3YW50IHRvIGV4dHJhY3QuCgpgYGB7ciBhc3NheS1jb3VudHMsIGxpdmUgPSBUUlVFfQpjb3VudHNfbWF0IDwtIGFzc2F5KGdlbmVfc3VtbWFyaXplZCwgImNvdW50cyIpCmBgYAoKV2UgY2FuIHVzZSB0aGUgYGNsYXNzYCBmdW5jdGlvbiB0byBzZWUgd2hhdCB0eXBlIG9mIG9iamVjdCB0aGUgYGFzc2F5KClgIGZ1bmN0aW9uIHJldHVybnMuCgpgYGB7ciBjbGFzcy1jb3VudHMsIGxpdmUgPSBUUlVFfQojIENoZWNrIHdoYXQgdHlwZSBvZiBvYmplY3QgYGNvdW50c19tYXRgIGlzCmNsYXNzKGNvdW50c19tYXQpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgZXh0cmFjdCB0aGUgVFBNIGRhdGEgLS0gY2FsbGVkIGBhYnVuZGFuY2VgIGZyb20gYGdlbmVfc3VtbWFyaXplZGAuCgpgYGB7ciBhc3NheS1hYnVuZGFuY2UsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBnZW5lLWxldmVsIFRQTQpoZWFkKGFzc2F5KGdlbmVfc3VtbWFyaXplZCwgImFidW5kYW5jZSIpKQpgYGAKCiMjIFNhdmUgdG8gZmlsZQoKV2UgY291bGQgdXNlIGByZWFkcjo6d3JpdGVfdHN2YCB0byBzYXZlIGBjb3VudHNfbWF0YCBvbmx5IGJ1dCBgZ2VuZV9zdW1tYXJpemVkYCBoYXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gc3RvcmVkIGhlcmUgYmV5b25kIHRoZSBjb3VudHMsIHNvIHdlIG1heSB3YW50IHRvIHNhdmUgYWxsIG9mIHRoaXMgdG8gYSBSRFMgb2JqZWN0LgoKYGBge3Igd3JpdGUtdHhpLCBsaXZlID0gVFJVRX0KIyBXcml0ZSBgZ2VuZV9zdW1tYXJpemVkYCB0byBSRFMgb2JqZWN0CnJlYWRyOjp3cml0ZV9yZHMoZ2VuZV9zdW1tYXJpemVkLCBmaWxlID0gdHhpX291dF9maWxlKQpgYGAKCldlJ2xsIGltcG9ydCB0aGlzIHdpdGggdGhlIGBERVNlcTJgIHBhY2thZ2UgaW4gdGhlIG5leHQgbm90ZWJvb2suCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
+LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGdlbmUtbGV2ZWwgc3VtbWFyaXphdGlvbiB3aXRoIGB0eGltZXRhYCIKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIEltcG9ydCBSTkEtc2VxIGV4cHJlc3Npb24gcXVhbnRpZmljYXRpb24gb3V0cHV0IHVzaW5nIGB0eGltZXRhYAotIFN1bW1hcml6ZSB0cmFuc2NyaXB0LWxldmVsIGV4cHJlc3Npb24gdG8gdGhlIGdlbmUgbGV2ZWwKLSBJbnRlcnJvZ2F0ZSBhbmQgZXh0cmFjdCBkYXRhIGZyb20gYSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdAoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBpbXBvcnQgdGhlIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiBxdWFudGlmaWNhdGlvbiBvdXRwdXQgZnJvbSBgc2FsbW9uIHF1YW50YCB1c2luZyB0aGUgW2B0eGltZXRhYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3R4aW1ldGEuaHRtbCkgcGFja2FnZS4KYHR4aW1ldGFgIGlzIGluIHBhcnQgYSB3cmFwcGVyIGFyb3VuZCBhbm90aGVyIHBhY2thZ2UsIFtgdHhpbXBvcnRgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvdHhpbXBvcnQuaHRtbCksIHdoaWNoIGltcG9ydHMgdHJhbnNjcmlwdCBleHByZXNzaW9uIGRhdGEgYW5kIHN1bW1hcml6ZXMgaXQgdG8gdGhlIGdlbmUgbGV2ZWwuCldvcmtpbmcgYXQgdGhlIGdlbmUgcmF0aGVyIHRoYW4gdHJhbnNjcmlwdCBsZXZlbCBoYXMgYSBudW1iZXIgb2YgcG90ZW50aWFsIGFkdmFudGFnZXMgZm9yIGludGVycHJldGFiaWxpdHksIGVmZmljaWVuY3ksIGFuZCByZWR1Y3Rpb24gb2YgZmFsc2UgcG9zaXRpdmVzIChbU29uZXNvbiBfZXQgYWwuXyAyMDE2XShodHRwczovL2RvaS5vcmcvMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43NTYzLjIpKS4KYHR4aW1ldGFgIGVhc2VzIHNvbWUgb2YgdGhlIGJ1cmRlbiBvZiBpbXBvcnQgYnkgYXV0b21hdGljYWxseSBpZGVudGlmeWluZyB0aGUgY29ycmVjdCBzZXQgb2YgYW5ub3RhdGlvbiBkYXRhIHRvIGFwcGVuZCB0byBtYW55IGRhdGEgc2V0cyAoW0xvdmUgX2V0IGFsLl8gMjAyMF0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDA3NjY0KSkuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV81LnBuZykKCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGB0eGltZXRhYCwgc2VlIFt0aGlzIGV4Y2VsbGVudCB2aWduZXR0ZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy90eGltZXRhL2luc3QvZG9jL3R4aW1ldGEuaHRtbCkgZnJvbSBMb3ZlIF9ldCBhbF8uCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeX0KIyBMb2FkIHRoZSB0eGltZXRhIHBhY2thZ2UKbGlicmFyeSh0eGltZXRhKQoKIyBMb2FkIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBwYWNrYWdlCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmBgYAoKIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpgYGB7ciBkaXJlY3RvcmllcywgbGl2ZSA9IFRSVUV9CiMgZGlyZWN0b3J5IHdoZXJlIHRoZSBkYXRhIGFyZSBsb2NhdGVkCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJnYXN0cmljLWNhbmNlciIpCgojIGRpcmVjdG9yeSB3aGVyZSB0aGUgcXVhbnQgZmlsZXMgYXJlIGxvY2F0ZWQsIGVhY2ggc2FtcGxlIGlzIGl0cyBvd24KIyBkaXJlY3RvcnkKcXVhbnRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInNhbG1vbl9xdWFudCIpCgojIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSB0eGltZXRhIHJlc3VsdHMgaWYgaXQgZG9lc24ndCBleGlzdCB5ZXQKdHhpX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJ0eGkiKQpmczo6ZGlyX2NyZWF0ZSh0eGlfZGlyKQpgYGAKCldlJ2xsIG5lZWQgdGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGFsbCB0aGUgc2FtcGxlcyBpbiBhbiBleHBlcmltZW50IHdoaWNoIHdlIGhhdmUgc3RvcmVkIGluIGBxdWFudF9kaXJgLgoKYGBge3IgaW5wdXQtbmFtZXN9CiMgdGhlIHF1YW50IGZpbGVzIHRoZW1zZWx2ZXMKc2ZfZmlsZXMgPC0gbGlzdC5maWxlcygKICBxdWFudF9kaXIsIAogIHJlY3Vyc2l2ZSA9IFRSVUUsIAogIGZ1bGwubmFtZXMgPSBUUlVFLAogIHBhdHRlcm4gPSAicXVhbnQuc2YiCikKYGBgCgpgYGB7ciBtZXRhZGF0YS1maWxlfQojIHNhbXBsZSBtZXRhZGF0YSBmaWxlCm1ldGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJnYXN0cmljLWNhbmNlcl9tZXRhZGF0YS50c3YiKQpgYGAKCioqT3V0cHV0KioKCmBgYHtyIG91dHB1dC1uYW1lcywgbGl2ZSA9IFRSVUV9CiMgTmFtZSB0aGUgb3V0cHV0IGdhc3RyaWMtY2FuY2VyX3R4aW1ldGEucmRzIGFuZCB1c2UgdGhlIGRpcmVjdG9yeSBjcmVhdGVkCiMgYWJvdmUgYXMgdGhlIHJlc3Qgb2YgdGhlIHBhdGgKdHhpX291dF9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5yZHMiKQpgYGAKCiMjIEZpbGUgbmFtZXMKCkFsbCBvdXRwdXQgZmlsZXMgZnJvbSBgc2FsbW9uIHF1YW50YCB3ZSdsbCB1c2Ugd2l0aCBgdHhpbWV0YWAgYXJlIG5hbWVkIGBxdWFudC5zZmAuClVuZm9ydHVuYXRlbHksIHRoaXMgbWVhbnMgdGhhdCB0aGUgZmlsZSBuYW1lcyB0aGVtc2VsdmVzIGRvIG5vdCBoYXZlIGFueSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlIHRoZXkgY29tZSBmcm9tIQoKYGBge3Igc2ZfZmlsZXMsIGxpdmUgPSBUUlVFfQojIExldCdzIGxvb2sgYXQgdGhlIGZ1bGwgcGF0aCBmb3IgdGhlIHF1YW50LnNmIGZpbGVzCnNmX2ZpbGVzCmBgYAoKTGV0J3MgZXh0cmFjdCB0aGUgX3NhbXBsZV8gbmFtZXMgZnJvbSB0aGUgKipmaWxlIHBhdGhzKiogdXNpbmcgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLgoKTm90aWNlIGhvdyB0aGUgZmlsZSBwYXRoIGlzIHNlcGFyYXRlZCBieSBgL2AuCklmIHdlIHdlcmUgdG8gc3BsaXQgdXAgdGhpcyBjaGFyYWN0ZXIgc3RyaW5nIGJ5IGAvYCwgdGhlIHNlY29uZCB0byBsYXN0IGl0ZW0gaXMgdGhlIHNhbXBsZSBuYW1lcyAoYmVjYXVzZSB3ZSB1c2VkIHRoZW0gYXMgZGlyZWN0b3J5IG5hbWVzIGZvciB0aGUgYHNhbG1vbmAgb3V0cHV0KS4KVGhpcyBpcyBleGFjdGx5IHdoYXQgYHN0cmluZ3I6OndvcmQoKWAgYWxsb3dzIHVzIHRvIGRvOiBzcGxpdCB1cCB0aGUgZmlsZSBwYXRocyBieSBgL2AgYW5kIGV4dHJhY3QgdGhlIHNhbXBsZSBuYW1lcy4KCmBgYHtyIHNhbXBsZV9uYW1lc30Kc2FtcGxlX25hbWVzIDwtIHN0cmluZ3I6OndvcmQoc2ZfZmlsZXMsIC0yLCBzZXAgPSAiLyIpCnNhbXBsZV9uYW1lcwpgYGAKCiMjIFNldCB1cCBtZXRhZGF0YQoKYHR4aW1ldGFgIG5lZWRzIGEgZGF0YSBmcmFtZSB3aXRoIGF0IGxlYXN0IHRoZXNlIHR3byBjb2x1bW5zOgotIGEgYGZpbGVzYCBjb2x1bW4gIHdpdGggdGhlIGZpbGUgcGF0aHMgdG8gdGhlIHF1YW50LnNmIGZpbGVzCi0gYSBgbmFtZXNgIGNvbHVtbiB3aXRoIHRoZSBzYW1wbGUgbmFtZXMKCmBgYHtyIG5hbWVzX3NmX2ZpbGVzfQpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoCiAgZmlsZXMgPSBzZl9maWxlcywKICBuYW1lcyA9IHNhbXBsZV9uYW1lcwopCmBgYAoKV2UgaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIHNhbXBsZXMgc3RvcmVkIGluIHRoZSBtZXRhZGF0YSBmaWxlIHRoYXQgd2Ugd2lsbCBhbHNvIHdhbnQgc3RvcmVkIGluIGBjb2xkYXRhYC4KTGV0J3MgcmVhZCBpbiB0aGUgc2FtcGxlIG1ldGFkYXRhIGZyb20gdGhlIFRTViBmaWxlLgoKYGBge3Igc2FtcGxlX21ldGFfZGYsIGxpdmUgPSBUUlVFfQojIFJlYWQgaW4gdGhlIHNhbXBsZSBtZXRhZGF0YSBUU1YgZmlsZSBhbmQgaGF2ZSBhIGxvb2sKc2FtcGxlX21ldGFfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KG1ldGFfZmlsZSkKc2FtcGxlX21ldGFfZGYKYGBgCgpXZSdsbCB3YW50IHRoaXMgaW5mb3JtYXRpb24gdG8gYmUgYWRkZWQgdG8gdGhlIGBjb2xkYXRhYCwgd2hpY2ggd2UgY2FuIGRvIGJ5IHVzaW5nIGEgam9pbiBmdW5jdGlvbiB0byBtYXRjaCB1cCB0aGUgcm93cyBiZXR3ZWVuIHRoZSB0d28gZGF0YSBmcmFtZXMgYW5kIGNvbWJpbmUgdGhlbS4KCmBgYHtyIGpvaW4tc2FtcGxlX21ldGFfZGZ9CmNvbGRhdGEgPC0gY29sZGF0YSB8PgogIGRwbHlyOjppbm5lcl9qb2luKHNhbXBsZV9tZXRhX2RmLCBieSA9IGMoIm5hbWVzIiA9ICJzcnJfYWNjZXNzaW9uIikpCgpjb2xkYXRhCmBgYAoKIyMgSW1wb3J0IGV4cHJlc3Npb24gZGF0YSB3aXRoIGB0eGltZXRhYAoKVXNpbmcgdGhlIGBjb2xkYXRhYCBkYXRhIGZyYW1lIHRoYXQgd2Ugc2V0IHVwLCB3ZSBjYW4gbm93IHJ1biB0aGUgYHR4aW1ldGEoKWAgdG8gaW1wb3J0IG91ciBleHByZXNzaW9uIGRhdGEgd2hpbGUgYXV0b21hdGljYWxseSBmaW5kaW5nIGFuZCBhc3NvY2lhdGluZyB0aGUgdHJhbnNjcmlwdCBhbm5vdGF0aW9ucyB0aGF0IHdlcmUgdXNlZCB3aGVuIHdlIHBlcmZvcm1lZCB0aGUgcXVhbnRpZmljYXRpb24uCgpUaGUgZmlyc3QgdGltZSB5b3UgcnVuIGB0eGltZXRhKClgIHlvdSBtYXkgZ2V0IGEgbWVzc2FnZSBhYm91dCBzdG9yaW5nIGRvd25sb2FkZWQgdHJhbnNjcmlwdG9tZSBkYXRhIGluIGEgY2FjaGUgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIHJldHJpZXZlIHRoZSBkYXRhIG1vcmUgcXVpY2tseSB0aGUgbmV4dCB0aW1lLgpXZSByZWNvbW1lbmQgeW91IHVzZSB0aGUgY2FjaGUsIGFuZCBhY2NlcHQgdGhlIGRlZmF1bHQgbG9jYXRpb24uCgpgYGB7ciB0eGltZXRhLCBsaXZlID0gVFJVRX0KdHhpX2RhdGEgPC0gdHhpbWV0YShjb2xkYXRhKQpgYGAKCip0eGltZXRhIGN1cnJlbnRseSB3b3JrcyBlYXNpbHkgZm9yIG1vc3QgaHVtYW4gYW5kIG1vdXNlIGRhdGFzZXRzLCBidXQgcmVxdWlyZXMgYSBbZmV3IG1vcmUgc3RlcHMgZm9yIG90aGVyIHNwZWNpZXNdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3R4aW1ldGEvaW5zdC9kb2MvdHhpbWV0YS5odG1sI1doYXRfaWZfY2hlY2tzdW1faXNuJUUyJTgwJTk5dF9rbm93bikuCgojIyBTdW1tYXJpemUgdG8gZ2VuZQoKV2UnbGwgc3VtbWFyaXplIHRvIHRoZSBnZW5lIGxldmVsIHVzaW5nIHRoZSBgc3VtbWFyaXplVG9HZW5lKClgIGZ1bmN0aW9uLgoKYGBge3Igc3VtbWFyaXplLWdlbmV9CiMgU3VtbWFyaXplIHRvIHRoZSBnZW5lIGxldmVsCmdlbmVfc3VtbWFyaXplZCA8LSBzdW1tYXJpemVUb0dlbmUodHhpX2RhdGEpCmBgYAoKV2UgY2FuIHVzZSB0aGUgYGNsYXNzYCBmdW5jdGlvbiB0byBzZWUgd2hhdCB0eXBlIG9mIG9iamVjdCBgZ2VuZV9zdW1tYXJpemVkYCBpcy4KCmBgYHtyIGNsYXNzLCBsaXZlID0gVFJVRX0KIyBDaGVjayB3aGF0IHR5cGUgb2Ygb2JqZWN0IGBnZW5lX3N1bW1hcml6ZWRgIGlzCmNsYXNzKGdlbmVfc3VtbWFyaXplZCkKYGBgCgpUaGlzIHRlbGxzIHVzIHRoYXQgYGdlbmVfc3VtbWFyaXplZGAgaXMgYW4gb2JqZWN0IGNhbGxlZCBhIFtgU3VtbWFyaXplZEV4cGVyaW1lbnRgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvU3VtbWFyaXplZEV4cGVyaW1lbnQvdmVyc2lvbnMvMS4yLjMvdG9waWNzL1N1bW1hcml6ZWRFeHBlcmltZW50LWNsYXNzKSB3aGljaCBjYW4gYmUgaGFuZGxlZCBieSBmdW5jdGlvbnMgZnJvbSB0aGUgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lLgpXZSBtb3JlIHNwZWNpZmljYWxseSBoYXZlIGEgYFJhbmdlZFN1bW1hcml6ZWRFeHBlcmltZW50YCB3aGljaCBpcyBhIG1vcmUgc3BlY2lmaWMgdHlwZSBvZiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgLgoKYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3RzIGhhdmUgdGhpcyBnZW5lcmFsIHN0cnVjdHVyZToKCiFbU3VtbWFyaXplZEV4cGVyaW1lbnRdKGRpYWdyYW1zL1N1bW1hcml6ZUV4cGVyaW1lbnQtc3RydWN0dXJlLnBuZykKClRoaXMgZmlndXJlIGlzIGZyb20gdGhpcyBoYW5keSB2aWduZXR0ZSBhYm91dCBbYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3RzXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL1N1bW1hcml6ZWRFeHBlcmltZW50L2luc3QvZG9jL1N1bW1hcml6ZWRFeHBlcmltZW50Lmh0bWwpLgoKQXMgc2hvd24gaW4gdGhlIGRpYWdyYW0sIHdlIGNhbiB1c2Ugc29tZSBvZiB0aGUgZnVuY3Rpb25zIHByb3ZpZGVkIGJ5IHRoZSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIHBhY2thZ2UgdG8gZXh0cmFjdCBkYXRhIGZyb20gb3VyIGBnZW5lX3N1bW1hcml6ZWRgIG9iamVjdC4KRm9yIGV4YW1wbGUsIGNhbGxpbmcgYHJvd0RhdGEoKWAgb24gb3VyIG9iamVjdCBzaG93cyBhbGwgdGhlIGdlbmUgaW5mb3JtYXRpb24gdGhhdCBgdHhpbWV0YWAgc2V0IHVwIQoKYGBge3J9CiMgcm93RGF0YSgpIHNob3dzIHVzIG91ciBnZW5lIGFubm90YXRpb24Kcm93RGF0YShnZW5lX3N1bW1hcml6ZWQpCmBgYAoKVGhlIGBhc3NheWAgc2xvdCBpbiBgU3VtbWFyaXplZEV4cGVyaW1lbnRgcyBob2xkcyBkYXRhIGZyb20gdGhlIGV4cGVyaW1lbnQuCkluIHRoaXMgY2FzZSwgaXQgd2lsbCBpbmNsdWRlIG91ciBnZW5lLWxldmVsIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gc3RvcmVkIGFzIGEgZ2VuZSB4IHNhbXBsZSBtYXRyaXguCgpNdWx0aXBsZSBgYXNzYXlzYCBjYW4gYmUgc3RvcmVkIGluIGFuIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgYW5kIHdlIGNhbiB1c2UgdGhlIGBhc3NheU5hbWVzKClgIGZ1bmN0aW9uIHRvIHNlZSB3aGF0IGFzc2F5cyBhcmUgaW5jbHVkZWQgaW4gYGdlbmVfc3VtbWFyaXplZGAuCgpgYGB7ciBhc3NheS1uYW1lcywgbGl2ZSA9IFRSVUV9CmFzc2F5TmFtZXMoZ2VuZV9zdW1tYXJpemVkKQpgYGAKCklmIHdlIHdhbnQgdG8gZXh0cmFjdCBhbiBgYXNzYXlgJ3MgZGF0YSwgd2UgY2FuIHVzZSBgYXNzYXkoKWAgZnVuY3Rpb24gYW5kIHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIGFzc2F5IHdlIHdhbnQgdG8gZXh0cmFjdC4KCmBgYHtyIGFzc2F5LWNvdW50cywgbGl2ZSA9IFRSVUV9CmNvdW50c19tYXQgPC0gYXNzYXkoZ2VuZV9zdW1tYXJpemVkLCAiY291bnRzIikKYGBgCgpXZSBjYW4gdXNlIHRoZSBgY2xhc3NgIGZ1bmN0aW9uIHRvIHNlZSB3aGF0IHR5cGUgb2Ygb2JqZWN0IHRoZSBgYXNzYXkoKWAgZnVuY3Rpb24gcmV0dXJucy4KCmBgYHtyIGNsYXNzLWNvdW50cywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0eXBlIG9mIG9iamVjdCBgY291bnRzX21hdGAgaXMKY2xhc3MoY291bnRzX21hdCkKYGBgCgpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBleHRyYWN0IHRoZSBUUE0gZGF0YSAtLSBjYWxsZWQgYGFidW5kYW5jZWAgZnJvbSBgZ2VuZV9zdW1tYXJpemVkYC4KCmBgYHtyIGFzc2F5LWFidW5kYW5jZSwgbGl2ZSA9IFRSVUV9CiMgTGV0J3MgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGdlbmUtbGV2ZWwgVFBNCmhlYWQoYXNzYXkoZ2VuZV9zdW1tYXJpemVkLCAiYWJ1bmRhbmNlIikpCmBgYAoKIyMgU2F2ZSB0byBmaWxlCgpXZSBjb3VsZCB1c2UgYHJlYWRyOjp3cml0ZV90c3ZgIHRvIHNhdmUgYGNvdW50c19tYXRgIG9ubHkgYnV0IGBnZW5lX3N1bW1hcml6ZWRgIGhhcyBhIGxvdCBvZiBpbmZvcm1hdGlvbiBzdG9yZWQgaGVyZSBiZXlvbmQgdGhlIGNvdW50cywgc28gd2UgbWF5IHdhbnQgdG8gc2F2ZSBhbGwgb2YgdGhpcyB0byBhIFJEUyBvYmplY3QuCgpgYGB7ciB3cml0ZS10eGksIGxpdmUgPSBUUlVFfQojIFdyaXRlIGBnZW5lX3N1bW1hcml6ZWRgIHRvIFJEUyBvYmplY3QKcmVhZHI6OndyaXRlX3JkcyhnZW5lX3N1bW1hcml6ZWQsIGZpbGUgPSB0eGlfb3V0X2ZpbGUpCmBgYAoKV2UnbGwgaW1wb3J0IHRoaXMgd2l0aCB0aGUgYERFU2VxMmAgcGFja2FnZSBpbiB0aGUgbmV4dCBub3RlYm9vay4KCiMjIFNlc3Npb24gSW5mbwoKUmVjb3JkIHNlc3Npb24gaW5mbyBmb3IgcmVwcm9kdWNpYmlsaXR5ICYgcHJvdmVuYW5jZSBwdXJwb3Nlcy4KCmBgYHtyIHNlc3Npb25pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=
diff --git a/RNA-seq/03-gastric_cancer_exploratory-live.Rmd b/RNA-seq/03-gastric_cancer_exploratory-live.Rmd
index 0277ed47..534795cf 100644
--- a/RNA-seq/03-gastric_cancer_exploratory-live.Rmd
+++ b/RNA-seq/03-gastric_cancer_exploratory-live.Rmd
@@ -43,7 +43,7 @@ data_dir <- file.path("data", "gastric-cancer")
# directory with the tximeta processed data
txi_dir <- file.path(data_dir, "txi")
-txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS")
+txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds")
```
We'll create a directory to hold our plots.
@@ -77,8 +77,7 @@ First, let's read in the data we processed with `tximeta`.
We use the tissue of origin in the design formula because that will allow us to model this variable of interest.
```{r ddset}
-ddset <- DESeqDataSet(gene_summarized,
- design = ~ tissue)
+ddset <- DESeqDataSet(gene_summarized, design = ~tissue)
```
### Variance stabilizing transformation
@@ -91,7 +90,7 @@ Since different samples are usually sequenced to different depths, we want to tr
We also want to deal with the fact that genes with low counts are also likely to have higher variance (on the log2 scale), as that could bias our clustering.
To handle both of these considerations, we can calculate a Variance Stabilizing Transformation of the count data, and work with that transformed data for our analysis.
-See [this section of the `DESeq2` vignette](http://bioconductor.org/packages/devel/bioc/vignettes/DESeq2/inst/doc/DESeq2.html#data-transformations-and-visualization) for more on this topic.
+See [this section of the `DESeq2` vignette](https://bioconductor.org/packages/release/bioc/vignettes/DESeq2/inst/doc/DESeq2.html#data-transformations-and-visualization) for more on this topic.
```{r vst}
vst_data <- vst(ddset)
@@ -112,15 +111,16 @@ Visualizing PC1 and PC2 can give us insight into how different variables (e.g.,
```{r plotPCA, live = TRUE}
-# DESeq2 built in function is called plotPCA and we want to color points by
-# tissue
+# DESeq2 built in function is called plotPCA and we want to
+# color points by tissue
```
Save the most recent plot to file with `ggsave` from `ggplot2`
```{r save-pdf}
-# Save the PDF file
+# Save plot as a PDF file
+# Note that `last_plot()` is the default, but we are making it explicit here
ggplot2::ggsave(pca_plot_file, plot = ggplot2::last_plot())
```
@@ -140,8 +140,8 @@ Now we can add a new column with toy batch information and re-store the `colData
```{r add-batch}
# Add batch information
-sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", "batch2",
- "batch1", "batch2", "batch1")
+sample_info$batch <- c("batch1", "batch1", "batch1", "batch1",
+ "batch2", "batch1", "batch2", "batch1")
```
If this batch information were real we would have included it with the sample metadata when we made the original `SummarizedExperiment` object with `tximeta`.
@@ -149,7 +149,7 @@ We would then include it in the model stored in our DESeq2 object using the `des
Here we will take a bit of a shortcut and add it directly to the `colData()` for our `vst()`-transformed data.
```{r coldata-vst, live = TRUE}
-# Add coldata() with batch info to vst_data
+# Add updated colData() with batch info to vst_data
```
diff --git a/RNA-seq/03-gastric_cancer_exploratory.nb.html b/RNA-seq/03-gastric_cancer_exploratory.nb.html
index b4d26555..a953eeb0 100644
--- a/RNA-seq/03-gastric_cancer_exploratory.nb.html
+++ b/RNA-seq/03-gastric_cancer_exploratory.nb.html
@@ -3031,25 +3031,23 @@ Libraries and functions
Directories and files
-
+
# Main data directory
data_dir <- file.path("data", "gastric-cancer")
# directory with the tximeta processed data
txi_dir <- file.path(data_dir, "txi")
-txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.RDS")
+txi_file <- file.path(txi_dir, "gastric-cancer_tximeta.rds")
We’ll create a directory to hold our plots.
- +# Create a plots directory if it does not exist yet
plots_dir <- file.path("plots", "gastric-cancer")
-if (!dir.exists(plots_dir)) {
- dir.create(plots_dir, recursive = TRUE)
-}
+fs::dir_create(plots_dir)
@@ -3085,9 +3083,8 @@ ddset <- DESeqDataSet(gene_summarized,
- design = ~ tissue)
+
+ddset <- DESeqDataSet(gene_summarized, design = ~tissue)
using counts and average transcript lengths from tximeta
@@ -3115,7 +3112,7 @@ See this
+ See this
section of the DESeq2
vignette for more on this
topic.Principal component analysis
and help us spot any technical effects (more on that below).
# DESeq2 built in function is called plotPCA and we want to color points by
-# tissue
+
+# DESeq2 built in function is called plotPCA and we want to
+# color points by tissue
plotPCA(vst_data, intgroup = "tissue")
using ntop=500 top features by variance
-
+
@@ -3168,8 +3165,9 @@ Principal component analysis
ggplot2
-
-# Save the PDF file
+
+# Save plot as a PDF file
+# Note that `last_plot()` is the default, but we are making it explicit here
ggplot2::ggsave(pca_plot_file, plot = ggplot2::last_plot())
@@ -3214,10 +3212,10 @@ A note on technical effects
the colData()
.
-
+
# Add batch information
-sample_info$batch <- c("batch1", "batch1", "batch1", "batch1", "batch2",
- "batch1", "batch2", "batch1")
+sample_info$batch <- c("batch1", "batch1", "batch1", "batch1",
+ "batch2", "batch1", "batch2", "batch1")
@@ -3232,20 +3230,22 @@ A note on technical effects
vst()
-transformed data.
-
-# Add coldata() with batch info to vst_data
+
+# Add updated colData() with batch info to vst_data
colData(vst_data) <- sample_info
-
+
# PCA plot - tissue *and* batch
# We want plotPCA to return the data so we can have more control about the plot
-pca_data <- plotPCA(vst_data,
- intgroup = c("tissue", "batch"),
- returnData = TRUE)
+pca_data <- plotPCA(
+ vst_data,
+ intgroup = c("tissue", "batch"),
+ returnData = TRUE
+)
using ntop=500 top features by variance
@@ -3263,18 +3263,23 @@ A note on technical effects
Let’s use ggplot to visualize the first two principal components.
-
+
# Color points by "batch" and use shape to indicate the tissue of origin
-ggplot2::ggplot(pca_data, ggplot2::aes(PC1, PC2,
- color = batch,
- shape = tissue)) +
+ggplot2::ggplot(pca_data,
+ ggplot2::aes(
+ x = PC1,
+ y = PC2,
+ color = batch,
+ shape = tissue
+ )
+) +
ggplot2::geom_point(size = 3) +
- ggplot2::xlab(paste0("PC1: ", percent_var[1],"% variance")) +
- ggplot2::ylab(paste0("PC2: ", percent_var[2],"% variance")) +
+ ggplot2::xlab(paste0("PC1: ", percent_var[1], "% variance")) +
+ ggplot2::ylab(paste0("PC2: ", percent_var[2], "% variance")) +
ggplot2::coord_fixed()
-
+
@@ -3288,8 +3293,8 @@ Session Info
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3323,18 +3328,18 @@ Session Info
loaded via a namespace (and not attached):
[1] gtable_0.3.5 xfun_0.43 bslib_0.7.0
[4] ggplot2_3.5.1 lattice_0.22-6 tzdb_0.4.0
- [7] vctrs_0.6.5 tools_4.4.0 generics_0.1.3
-[10] parallel_4.4.0 getopt_1.20.4 tibble_3.2.1
+ [7] vctrs_0.6.5 tools_4.4.1 generics_0.1.3
+[10] parallel_4.4.1 getopt_1.20.4 tibble_3.2.1
[13] fansi_1.0.6 highr_0.10 pkgconfig_2.0.3
[16] Matrix_1.7-0 lifecycle_1.0.4 GenomeInfoDbData_1.2.12
-[19] farver_2.1.1 compiler_4.4.0 stringr_1.5.1
+[19] farver_2.1.1 compiler_4.4.1 stringr_1.5.1
[22] textshaping_0.3.7 munsell_0.5.1 codetools_0.2-20
[25] htmltools_0.5.8.1 sass_0.4.9 yaml_2.3.8
[28] pillar_1.9.0 crayon_1.5.2 jquerylib_0.1.4
[31] BiocParallel_1.38.0 DelayedArray_0.30.0 cachem_1.0.8
[34] abind_1.4-5 locfit_1.5-9.9 tidyselect_1.2.1
[37] digest_0.6.35 stringi_1.8.3 dplyr_1.1.4
-[40] labeling_0.4.3 fastmap_1.1.1 grid_4.4.0
+[40] labeling_0.4.3 fastmap_1.1.1 grid_4.4.1
[43] colorspace_2.1-0 cli_3.6.2 SparseArray_1.4.0
[46] magrittr_2.0.3 S4Arrays_1.4.0 utf8_1.2.4
[49] withr_3.0.0 readr_2.1.5 UCSC.utils_1.0.0
@@ -3342,12 +3347,13 @@ Session Info
[55] httr_1.4.7 ragg_1.3.0 hms_1.1.3
[58] evaluate_0.23 knitr_1.46 rlang_1.1.3
[61] Rcpp_1.0.12 glue_1.7.0 jsonlite_1.8.8
-[64] R6_2.5.1 systemfonts_1.0.6 zlibbioc_1.50.0
+[64] R6_2.5.1 systemfonts_1.0.6 fs_1.6.4
+[67] zlibbioc_1.50.0
-LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGV4cGxvcmF0b3J5IGFuYWx5c2lzIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyMgT2JqZWN0aXZlcwoKVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0bzoKCi0gQ3JlYXRlIGEgYERFU2VxMmAgZGF0YSBzZXQgZnJvbSBhIGBTdW1tYXJpemVkRXhwZXJpbWVudGAKLSBUcmFuc2Zvcm0gUk5BLXNlcSBjb3VudCBkYXRhIHdpdGggYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbgotIENyZWF0ZSBQQ0EgcGxvdHMgdG8gZXhwbG9yZSBzdHJ1Y3R1cmUgYW1vbmcgUk5BLXNlcSBzYW1wbGVzCgotLS0KCkluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIGltcG9ydCB0aGUgZ2FzdHJpYyBjYW5jZXIgZGF0YSBhbmQgZG8gc29tZSBleHBsb3JhdG9yeQphbmFseXNlcyBhbmQgdmlzdWFsIGluc3BlY3Rpb24uCldlJ2xsIHVzZSB0aGUgW2BERVNlcTJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIHBhY2thZ2UgZm9yIHRoaXMuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCmBERVNlcTJgIGFsc28gaGFzIGFuIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkKZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlci4gX0dlbm9tZSBCaW9sb2d5Xy4gMjAxNC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeSwgbGl2ZSA9IFRSVUV9CiMgTG9hZCB0aGUgREVTZXEyIGxpYnJhcnkKbGlicmFyeShERVNlcTIpCmBgYAoKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKYGBge3IgaW5wdXQtZmlsZXN9CiMgTWFpbiBkYXRhIGRpcmVjdG9yeQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2FzdHJpYy1jYW5jZXIiKQoKIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5SRFMiKQpgYGAKCldlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXMgbm90IGV4aXN0IHlldApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJnYXN0cmljLWNhbmNlciIpCmlmICghZGlyLmV4aXN0cyhwbG90c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShwbG90c19kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgoqKk91dHB1dCoqCgpgYGB7ciBvdXRwdXQtZmlsZXMsIGxpdmUgPSBUUlVFfQojIFdlIHdpbGwgc2F2ZSBhIFBERiBjb3B5IG9mIHRoZSBQQ0EgcGxvdCB0byB0aGUgcGxvdHMgZGlyZWN0b3J5CiMgYW5kIG5hbWUgdGhlIGZpbGUgImdhc3RyaWMtY2FuY2VyX1BDX3NjYXR0ZXIucGRmIgpwY2FfcGxvdF9maWxlIDwtIGZpbGUucGF0aChwbG90c19kaXIsICJnYXN0cmljLWNhbmNlcl9QQ19zY2F0dGVyLnBkZiIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIGEgdHhpbWV0YSBvYmplY3QKCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBkYXRhIHdlIHByb2Nlc3NlZCB3aXRoIGB0eGltZXRhYC4KCmBgYHtyIHJlYWQtcmRzLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBSRFMgZmlsZSB3ZSBjcmVhdGVkIGluIHRoZSBsYXN0IG5vdGVib29rCmdlbmVfc3VtbWFyaXplZCA8LSByZWFkcjo6cmVhZF9yZHModHhpX2ZpbGUpCmBgYAoKIyMjIFNldCB1cCBERVNlcTIgb2JqZWN0CgpXZSB1c2UgdGhlIHRpc3N1ZSBvZiBvcmlnaW4gaW4gdGhlIGRlc2lnbiBmb3JtdWxhIGJlY2F1c2UgdGhhdCB3aWxsIGFsbG93IHVzIHRvIG1vZGVsIHRoaXMgdmFyaWFibGUgb2YgaW50ZXJlc3QuCgpgYGB7ciBkZHNldH0KZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gdGlzc3VlKQpgYGAKCiMjIyBWYXJpYW5jZSBzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbgoKUmF3IGNvdW50IGRhdGEgaXMgbm90IHVzdWFsbHkgc3VpdGFibGUgZm9yIHRoZSBhbGdvcml0aG1zIHdlIHVzZSBmb3IgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLCBjbHVzdGVyaW5nLCBvciBoZWF0bWFwcy4KVG8gaW1wcm92ZSB0aGlzLCB3ZSB3aWxsIHRyYW5zZm9ybSB0aGUgY291bnQgZGF0YSB0byBjcmVhdGUgYW4gZXhwcmVzc2lvbiBtZWFzdXJlIHRoYXQgaXMgYmV0dGVyIHN1aXRlZCBmb3IgdGhlc2UgYW5hbHlzZXMuClRoZSBjb3JlIHRyYW5zZm9ybWF0aW9uIHdpbGwgbWFwIHRoZSBleHByZXNzaW9uIHRvIGEgbG9nMiBzY2FsZSwgd2hpbGUgYWNjb3VudGluZyBmb3Igc29tZSBvZiB0aGUgZXhwZWN0ZWQgdmFyaWF0aW9uIGFtb25nIHNhbXBsZXMgYW5kIGdlbmVzLgoKU2luY2UgZGlmZmVyZW50IHNhbXBsZXMgYXJlIHVzdWFsbHkgc2VxdWVuY2VkIHRvIGRpZmZlcmVudCBkZXB0aHMsIHdlIHdhbnQgdG8gdHJhbnNmb3JtIG91ciBSTkEtc2VxIGNvdW50IGRhdGEgdG8gbWFrZSBkaWZmZXJlbnQgc2FtcGxlcyBtb3JlIGRpcmVjdGx5IGNvbXBhcmFibGUuCldlIGFsc28gd2FudCB0byBkZWFsIHdpdGggdGhlIGZhY3QgdGhhdCBnZW5lcyB3aXRoIGxvdyBjb3VudHMgYXJlIGFsc28gbGlrZWx5IHRvIGhhdmUgaGlnaGVyIHZhcmlhbmNlIChvbiB0aGUgbG9nMiBzY2FsZSksIGFzIHRoYXQgY291bGQgYmlhcyBvdXIgY2x1c3RlcmluZy4KVG8gaGFuZGxlIGJvdGggb2YgdGhlc2UgY29uc2lkZXJhdGlvbnMsIHdlIGNhbiBjYWxjdWxhdGUgYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgZGF0YSwgYW5kIHdvcmsgd2l0aCB0aGF0IHRyYW5zZm9ybWVkIGRhdGEgZm9yIG91ciBhbmFseXNpcy4KClNlZSBbdGhpcyBzZWN0aW9uIG9mIHRoZSBgREVTZXEyYCB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2RhdGEtdHJhbnNmb3JtYXRpb25zLWFuZC12aXN1YWxpemF0aW9uKSBmb3IgbW9yZSBvbiB0aGlzIHRvcGljLgoKYGBge3IgdnN0fQp2c3RfZGF0YSA8LSB2c3QoZGRzZXQpCmBgYAoKIyMjIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMKClByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgaXMgYSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdGVjaG5pcXVlIHRoYXQgYWxsb3dzIHVzIHRvIGlkZW50aWZ5IHRoZSBsYXJnZXN0IGNvbXBvbmVudHMgb2YgdmFyaWF0aW9uIGluIGEgY29tcGxleCBkYXRhc2V0LgpPdXIgZXhwcmVzc2lvbiBkYXRhIGNhbiBiZSB0aG91Z2h0IG9mIGFzIG1hcHBpbmcgZWFjaCBzYW1wbGUgaW4gYSBtdWx0aWRpbWVuc2lvbmFsIHNwYWNlIGRlZmluZWQgYnkgdGhlIGV4cHJlc3Npb24gbGV2ZWwgb2YgZWFjaCBnZW5lLgpUaGUgZXhwcmVzc2lvbiBvZiBtYW55IG9mIHRob3NlIGdlbmVzIGFyZSBjb3JyZWxhdGVkLCBzbyB3ZSBjYW4gb2Z0ZW4gZ2V0IGEgYmV0dGVyLCBzaW1wbGVyIHBpY3R1cmUgb2YgdGhlIGRhdGEgYnkgY29tYmluaW5nIHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRob3NlIGNvcnJlbGF0ZWQgZ2VuZXMuCgpQQ0Egcm90YXRlcyBhbmQgdHJhbnNmb3JtcyB0aGlzIHNwYWNlIHNvIHRoYXQgZWFjaCBheGlzIGlzIG5vdyBhIGNvbWJpbmF0aW9uIG9mIG11bHRpcGxlIGNvcnJlbGF0ZWQgZ2VuZXMsIG9yZGVyZWQgc28gdGhlIGZpcnN0IGF4ZXMgY2FwdHVyZSB0aGUgbW9zdCB2YXJpYXRpb24gZnJvbSB0aGUgZGF0YS4KVGhlc2UgbmV3IGF4ZXMgYXJlIHRoZSAicHJpbmNpcGFsIGNvbXBvbmVudHMuIgpJZiB3ZSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgY29tcG9uZW50cywgd2UgY2FuIG9mdGVuIGdldCBhIG5pY2Ugb3ZlcnZpZXcgb2YgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgc2FtcGxlcyBpbiB0aGUgZGF0YS4KClRoZSBgcGxvdFBDQSgpYCBmdW5jdGlvbiB3ZSB3aWxsIHVzZSBmcm9tIHRoZSBgREVTZXEyYCBwYWNrYWdlIGNhbGN1bGF0ZXMgYW5kIHBsb3RzIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDMSBhbmQgUEMyKS4KVmlzdWFsaXppbmcgUEMxIGFuZCBQQzIgY2FuIGdpdmUgdXMgaW5zaWdodCBpbnRvIGhvdyBkaWZmZXJlbnQgdmFyaWFibGVzIChlLmcuLCB0aXNzdWUgc291cmNlKSBhZmZlY3Qgb3VyIGRhdGFzZXQgYW5kIGhlbHAgdXMgc3BvdCBhbnkgdGVjaG5pY2FsIGVmZmVjdHMgKG1vcmUgb24gdGhhdCBiZWxvdykuCgoKYGBge3IgcGxvdFBDQSwgbGl2ZSA9IFRSVUV9CiMgREVTZXEyIGJ1aWx0IGluIGZ1bmN0aW9uIGlzIGNhbGxlZCBwbG90UENBIGFuZCB3ZSB3YW50IHRvIGNvbG9yIHBvaW50cyBieQojIHRpc3N1ZQpwbG90UENBKHZzdF9kYXRhLCBpbnRncm91cCA9ICJ0aXNzdWUiKQpgYGAKClNhdmUgdGhlIG1vc3QgcmVjZW50IHBsb3QgdG8gZmlsZSB3aXRoIGBnZ3NhdmVgIGZyb20gYGdncGxvdDJgCgpgYGB7ciBzYXZlLXBkZn0KIyBTYXZlIHRoZSBQREYgZmlsZQpnZ3Bsb3QyOjpnZ3NhdmUocGNhX3Bsb3RfZmlsZSwgcGxvdCA9IGdncGxvdDI6Omxhc3RfcGxvdCgpKQpgYGAKCiMjIEEgbm90ZSBvbiB0ZWNobmljYWwgZWZmZWN0cwoKV2UgZG9uJ3QgaGF2ZSBiYXRjaCBpbmZvcm1hdGlvbiAoaS5lLiwgd2hlbiB0aGUgc2FtcGxlcyB3ZXJlIHJ1bikgZm9yIHRoaXMgcGFydGljdWxhciBleHBlcmltZW50LCBidXQgbGV0J3MgaW1hZ2luZSB0aGF0IGBTUlI1ODU1NzRgIGFuZCBgU1JSNTg1NTc2YCB3ZXJlIHJ1biBzZXBhcmF0ZWx5IGZyb20gYWxsIG90aGVyIHNhbXBsZXMuCldlJ2xsIGFkZCB0aGlzIGFzIGEgbmV3ICJ0b3kiIGNvbHVtbiBpbiB0aGUgc2FtcGxlIGRhdGEgKGBjb2xEYXRhYCkuCgpgYGB7ciBleHRyYWN0LXNhbXBsZSwgbGl2ZSA9IFRSVUV9CiMgRXh0cmFjdCBjb2xEYXRhCnNhbXBsZV9pbmZvIDwtIGNvbERhdGEodnN0X2RhdGEpCgojIFByaW50IG91dCBwcmV2aWV3CnNhbXBsZV9pbmZvCmBgYAoKTm93IHdlIGNhbiBhZGQgYSBuZXcgY29sdW1uIHdpdGggdG95IGJhdGNoIGluZm9ybWF0aW9uIGFuZCByZS1zdG9yZSB0aGUgYGNvbERhdGEoKWAuCgpgYGB7ciBhZGQtYmF0Y2h9CiMgQWRkIGJhdGNoIGluZm9ybWF0aW9uCnNhbXBsZV9pbmZvJGJhdGNoIDwtIGMoImJhdGNoMSIsICJiYXRjaDEiLCAiYmF0Y2gxIiwgImJhdGNoMSIsICJiYXRjaDIiLAogICAgICAgICAgICAgICAgICAgICAgICJiYXRjaDEiLCAiYmF0Y2gyIiwgImJhdGNoMSIpCmBgYAoKSWYgdGhpcyBiYXRjaCBpbmZvcm1hdGlvbiB3ZXJlIHJlYWwgd2Ugd291bGQgaGF2ZSBpbmNsdWRlZCBpdCB3aXRoIHRoZSBzYW1wbGUgbWV0YWRhdGEgd2hlbiB3ZSBtYWRlIHRoZSBvcmlnaW5hbCBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIG9iamVjdCB3aXRoIGB0eGltZXRhYC4KV2Ugd291bGQgdGhlbiBpbmNsdWRlIGl0IGluIHRoZSBtb2RlbCBzdG9yZWQgaW4gb3VyIERFU2VxMiBvYmplY3QgdXNpbmcgdGhlIGBkZXNpZ25gIGFyZ3VtZW50IChgZGVzaWduID0gfiB0aXNzdWUgKyBiYXRjaGApIGFuZCB3ZSB3b3VsZCByZS1ydW4gdGhlIGBERVNlcURhdGFTZXQoKWAgYW5kIGB2c3QoKWAgc3RlcHMgd2UgZGlkIGFib3ZlLgpIZXJlIHdlIHdpbGwgdGFrZSBhIGJpdCBvZiBhIHNob3J0Y3V0IGFuZCBhZGQgaXQgZGlyZWN0bHkgdG8gdGhlIGBjb2xEYXRhKClgIGZvciBvdXIgYHZzdCgpYC10cmFuc2Zvcm1lZCBkYXRhLgoKYGBge3IgY29sZGF0YS12c3QsIGxpdmUgPSBUUlVFfQojIEFkZCBjb2xkYXRhKCkgd2l0aCBiYXRjaCBpbmZvIHRvIHZzdF9kYXRhCmNvbERhdGEodnN0X2RhdGEpIDwtIHNhbXBsZV9pbmZvCmBgYAoKYGBge3IgcGxvdFBDQS0yLCBsaXZlID0gVFJVRX0KIyBQQ0EgcGxvdCAtIHRpc3N1ZSAqYW5kKiBiYXRjaAojIFdlIHdhbnQgcGxvdFBDQSB0byByZXR1cm4gdGhlIGRhdGEgc28gd2UgY2FuIGhhdmUgbW9yZSBjb250cm9sIGFib3V0IHRoZSBwbG90CnBjYV9kYXRhIDwtIHBsb3RQQ0EodnN0X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0aXNzdWUiLCAiYmF0Y2giKSwKICAgICAgICAgICAgICAgICAgICByZXR1cm5EYXRhID0gVFJVRSkKYGBgCgpgYGB7ciBwZXJjZW50X3Zhcn0KIyBIZXJlIHdlIGFyZSBzZXR0aW5nIHVwIHRoZSBwZXJjZW50IHZhcmlhbmNlIHRoYXQgd2UgYXJlIGV4dHJhY3RpbmcgZnJvbSB0aGUgYHBjYV9kYXRhYCBvYmplY3QKcGVyY2VudF92YXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FfZGF0YSwgInBlcmNlbnRWYXIiKSkKYGBgCgpMZXQncyB1c2UgZ2dwbG90IHRvIHZpc3VhbGl6ZSB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzLgoKYGBge3IgY29sb3ItYnktYmF0Y2gsIGxpdmUgPSBUUlVFfQojIENvbG9yIHBvaW50cyBieSAiYmF0Y2giIGFuZCB1c2Ugc2hhcGUgdG8gaW5kaWNhdGUgdGhlIHRpc3N1ZSBvZiBvcmlnaW4KZ2dwbG90Mjo6Z2dwbG90KHBjYV9kYXRhLCBnZ3Bsb3QyOjphZXMoUEMxLCBQQzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmF0Y2gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gdGlzc3VlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZ3Bsb3QyOjp4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50X3ZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgZ2dwbG90Mjo6eWxhYihwYXN0ZTAoIlBDMjogIiwgcGVyY2VudF92YXJbMl0sIiUgdmFyaWFuY2UiKSkgKwogIGdncGxvdDI6OmNvb3JkX2ZpeGVkKCkKYGBgCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
+LS0tCnRpdGxlOiAiR2FzdHJpYyBjYW5jZXI6IGV4cGxvcmF0b3J5IGFuYWx5c2lzIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyMgT2JqZWN0aXZlcwoKVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0bzoKCi0gQ3JlYXRlIGEgYERFU2VxMmAgZGF0YSBzZXQgZnJvbSBhIGBTdW1tYXJpemVkRXhwZXJpbWVudGAKLSBUcmFuc2Zvcm0gUk5BLXNlcSBjb3VudCBkYXRhIHdpdGggYSBWYXJpYW5jZSBTdGFiaWxpemluZyBUcmFuc2Zvcm1hdGlvbgotIENyZWF0ZSBQQ0EgcGxvdHMgdG8gZXhwbG9yZSBzdHJ1Y3R1cmUgYW1vbmcgUk5BLXNlcSBzYW1wbGVzCgotLS0KCkluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIGltcG9ydCB0aGUgZ2FzdHJpYyBjYW5jZXIgZGF0YSBhbmQgZG8gc29tZSBleHBsb3JhdG9yeQphbmFseXNlcyBhbmQgdmlzdWFsIGluc3BlY3Rpb24uCldlJ2xsIHVzZSB0aGUgW2BERVNlcTJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIHBhY2thZ2UgZm9yIHRoaXMuCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCmBERVNlcTJgIGFsc28gaGFzIGFuIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkKZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlci4gX0dlbm9tZSBCaW9sb2d5Xy4gMjAxNC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgojIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3IgbGlicmFyeSwgbGl2ZSA9IFRSVUV9CiMgTG9hZCB0aGUgREVTZXEyIGxpYnJhcnkKbGlicmFyeShERVNlcTIpCmBgYAoKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKYGBge3IgaW5wdXQtZmlsZXN9CiMgTWFpbiBkYXRhIGRpcmVjdG9yeQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2FzdHJpYy1jYW5jZXIiKQoKIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiZ2FzdHJpYy1jYW5jZXJfdHhpbWV0YS5yZHMiKQpgYGAKCldlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXMgbm90IGV4aXN0IHlldApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJnYXN0cmljLWNhbmNlciIpCmZzOjpkaXJfY3JlYXRlKHBsb3RzX2RpcikKYGBgCgoqKk91dHB1dCoqCgpgYGB7ciBvdXRwdXQtZmlsZXMsIGxpdmUgPSBUUlVFfQojIFdlIHdpbGwgc2F2ZSBhIFBERiBjb3B5IG9mIHRoZSBQQ0EgcGxvdCB0byB0aGUgcGxvdHMgZGlyZWN0b3J5CiMgYW5kIG5hbWUgdGhlIGZpbGUgImdhc3RyaWMtY2FuY2VyX1BDX3NjYXR0ZXIucGRmIgpwY2FfcGxvdF9maWxlIDwtIGZpbGUucGF0aChwbG90c19kaXIsICJnYXN0cmljLWNhbmNlcl9QQ19zY2F0dGVyLnBkZiIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIGEgdHhpbWV0YSBvYmplY3QKCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBkYXRhIHdlIHByb2Nlc3NlZCB3aXRoIGB0eGltZXRhYC4KCmBgYHtyIHJlYWQtcmRzLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBSRFMgZmlsZSB3ZSBjcmVhdGVkIGluIHRoZSBsYXN0IG5vdGVib29rCmdlbmVfc3VtbWFyaXplZCA8LSByZWFkcjo6cmVhZF9yZHModHhpX2ZpbGUpCmBgYAoKIyMjIFNldCB1cCBERVNlcTIgb2JqZWN0CgpXZSB1c2UgdGhlIHRpc3N1ZSBvZiBvcmlnaW4gaW4gdGhlIGRlc2lnbiBmb3JtdWxhIGJlY2F1c2UgdGhhdCB3aWxsIGFsbG93IHVzIHRvIG1vZGVsIHRoaXMgdmFyaWFibGUgb2YgaW50ZXJlc3QuCgpgYGB7ciBkZHNldH0KZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwgZGVzaWduID0gfnRpc3N1ZSkKYGBgCgojIyMgVmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24KClJhdyBjb3VudCBkYXRhIGlzIG5vdCB1c3VhbGx5IHN1aXRhYmxlIGZvciB0aGUgYWxnb3JpdGhtcyB3ZSB1c2UgZm9yIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiwgY2x1c3RlcmluZywgb3IgaGVhdG1hcHMuClRvIGltcHJvdmUgdGhpcywgd2Ugd2lsbCB0cmFuc2Zvcm0gdGhlIGNvdW50IGRhdGEgdG8gY3JlYXRlIGFuIGV4cHJlc3Npb24gbWVhc3VyZSB0aGF0IGlzIGJldHRlciBzdWl0ZWQgZm9yIHRoZXNlIGFuYWx5c2VzLgpUaGUgY29yZSB0cmFuc2Zvcm1hdGlvbiB3aWxsIG1hcCB0aGUgZXhwcmVzc2lvbiB0byBhIGxvZzIgc2NhbGUsIHdoaWxlIGFjY291bnRpbmcgZm9yIHNvbWUgb2YgdGhlIGV4cGVjdGVkIHZhcmlhdGlvbiBhbW9uZyBzYW1wbGVzIGFuZCBnZW5lcy4KClNpbmNlIGRpZmZlcmVudCBzYW1wbGVzIGFyZSB1c3VhbGx5IHNlcXVlbmNlZCB0byBkaWZmZXJlbnQgZGVwdGhzLCB3ZSB3YW50IHRvIHRyYW5zZm9ybSBvdXIgUk5BLXNlcSBjb3VudCBkYXRhIHRvIG1ha2UgZGlmZmVyZW50IHNhbXBsZXMgbW9yZSBkaXJlY3RseSBjb21wYXJhYmxlLgpXZSBhbHNvIHdhbnQgdG8gZGVhbCB3aXRoIHRoZSBmYWN0IHRoYXQgZ2VuZXMgd2l0aCBsb3cgY291bnRzIGFyZSBhbHNvIGxpa2VseSB0byBoYXZlIGhpZ2hlciB2YXJpYW5jZSAob24gdGhlIGxvZzIgc2NhbGUpLCBhcyB0aGF0IGNvdWxkIGJpYXMgb3VyIGNsdXN0ZXJpbmcuClRvIGhhbmRsZSBib3RoIG9mIHRoZXNlIGNvbnNpZGVyYXRpb25zLCB3ZSBjYW4gY2FsY3VsYXRlIGEgVmFyaWFuY2UgU3RhYmlsaXppbmcgVHJhbnNmb3JtYXRpb24gb2YgdGhlIGNvdW50IGRhdGEsIGFuZCB3b3JrIHdpdGggdGhhdCB0cmFuc2Zvcm1lZCBkYXRhIGZvciBvdXIgYW5hbHlzaXMuCgpTZWUgW3RoaXMgc2VjdGlvbiBvZiB0aGUgYERFU2VxMmAgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNkYXRhLXRyYW5zZm9ybWF0aW9ucy1hbmQtdmlzdWFsaXphdGlvbikgZm9yIG1vcmUgb24gdGhpcyB0b3BpYy4KCmBgYHtyIHZzdH0KdnN0X2RhdGEgPC0gdnN0KGRkc2V0KQpgYGAKCiMjIyBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzCgpQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIGlzIGEgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHRlY2huaXF1ZSB0aGF0IGFsbG93cyB1cyB0byBpZGVudGlmeSB0aGUgbGFyZ2VzdCBjb21wb25lbnRzIG9mIHZhcmlhdGlvbiBpbiBhIGNvbXBsZXggZGF0YXNldC4KT3VyIGV4cHJlc3Npb24gZGF0YSBjYW4gYmUgdGhvdWdodCBvZiBhcyBtYXBwaW5nIGVhY2ggc2FtcGxlIGluIGEgbXVsdGlkaW1lbnNpb25hbCBzcGFjZSBkZWZpbmVkIGJ5IHRoZSBleHByZXNzaW9uIGxldmVsIG9mIGVhY2ggZ2VuZS4KVGhlIGV4cHJlc3Npb24gb2YgbWFueSBvZiB0aG9zZSBnZW5lcyBhcmUgY29ycmVsYXRlZCwgc28gd2UgY2FuIG9mdGVuIGdldCBhIGJldHRlciwgc2ltcGxlciBwaWN0dXJlIG9mIHRoZSBkYXRhIGJ5IGNvbWJpbmluZyB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aG9zZSBjb3JyZWxhdGVkIGdlbmVzLgoKUENBIHJvdGF0ZXMgYW5kIHRyYW5zZm9ybXMgdGhpcyBzcGFjZSBzbyB0aGF0IGVhY2ggYXhpcyBpcyBub3cgYSBjb21iaW5hdGlvbiBvZiBtdWx0aXBsZSBjb3JyZWxhdGVkIGdlbmVzLCBvcmRlcmVkIHNvIHRoZSBmaXJzdCBheGVzIGNhcHR1cmUgdGhlIG1vc3QgdmFyaWF0aW9uIGZyb20gdGhlIGRhdGEuClRoZXNlIG5ldyBheGVzIGFyZSB0aGUgInByaW5jaXBhbCBjb21wb25lbnRzLiIKSWYgd2UgbG9vayBhdCB0aGUgZmlyc3QgZmV3IGNvbXBvbmVudHMsIHdlIGNhbiBvZnRlbiBnZXQgYSBuaWNlIG92ZXJ2aWV3IG9mIHJlbGF0aW9uc2hpcHMgYW1vbmcgdGhlIHNhbXBsZXMgaW4gdGhlIGRhdGEuCgpUaGUgYHBsb3RQQ0EoKWAgZnVuY3Rpb24gd2Ugd2lsbCB1c2UgZnJvbSB0aGUgYERFU2VxMmAgcGFja2FnZSBjYWxjdWxhdGVzIGFuZCBwbG90cyB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIChQQzEgYW5kIFBDMikuClZpc3VhbGl6aW5nIFBDMSBhbmQgUEMyIGNhbiBnaXZlIHVzIGluc2lnaHQgaW50byBob3cgZGlmZmVyZW50IHZhcmlhYmxlcyAoZS5nLiwgdGlzc3VlIHNvdXJjZSkgYWZmZWN0IG91ciBkYXRhc2V0IGFuZCBoZWxwIHVzIHNwb3QgYW55IHRlY2huaWNhbCBlZmZlY3RzIChtb3JlIG9uIHRoYXQgYmVsb3cpLgoKCmBgYHtyIHBsb3RQQ0EsIGxpdmUgPSBUUlVFfQojIERFU2VxMiBidWlsdCBpbiBmdW5jdGlvbiBpcyBjYWxsZWQgcGxvdFBDQSBhbmQgd2Ugd2FudCB0byAKIyBjb2xvciBwb2ludHMgYnkgdGlzc3VlCnBsb3RQQ0EodnN0X2RhdGEsIGludGdyb3VwID0gInRpc3N1ZSIpCmBgYAoKU2F2ZSB0aGUgbW9zdCByZWNlbnQgcGxvdCB0byBmaWxlIHdpdGggYGdnc2F2ZWAgZnJvbSBgZ2dwbG90MmAKCmBgYHtyIHNhdmUtcGRmfQojIFNhdmUgcGxvdCBhcyBhIFBERiBmaWxlCiMgTm90ZSB0aGF0IGBsYXN0X3Bsb3QoKWAgaXMgdGhlIGRlZmF1bHQsIGJ1dCB3ZSBhcmUgbWFraW5nIGl0IGV4cGxpY2l0IGhlcmUKZ2dwbG90Mjo6Z2dzYXZlKHBjYV9wbG90X2ZpbGUsIHBsb3QgPSBnZ3Bsb3QyOjpsYXN0X3Bsb3QoKSkKYGBgCgojIyBBIG5vdGUgb24gdGVjaG5pY2FsIGVmZmVjdHMKCldlIGRvbid0IGhhdmUgYmF0Y2ggaW5mb3JtYXRpb24gKGkuZS4sIHdoZW4gdGhlIHNhbXBsZXMgd2VyZSBydW4pIGZvciB0aGlzIHBhcnRpY3VsYXIgZXhwZXJpbWVudCwgYnV0IGxldCdzIGltYWdpbmUgdGhhdCBgU1JSNTg1NTc0YCBhbmQgYFNSUjU4NTU3NmAgd2VyZSBydW4gc2VwYXJhdGVseSBmcm9tIGFsbCBvdGhlciBzYW1wbGVzLgpXZSdsbCBhZGQgdGhpcyBhcyBhIG5ldyAidG95IiBjb2x1bW4gaW4gdGhlIHNhbXBsZSBkYXRhIChgY29sRGF0YWApLgoKYGBge3IgZXh0cmFjdC1zYW1wbGUsIGxpdmUgPSBUUlVFfQojIEV4dHJhY3QgY29sRGF0YQpzYW1wbGVfaW5mbyA8LSBjb2xEYXRhKHZzdF9kYXRhKQoKIyBQcmludCBvdXQgcHJldmlldwpzYW1wbGVfaW5mbwpgYGAKCk5vdyB3ZSBjYW4gYWRkIGEgbmV3IGNvbHVtbiB3aXRoIHRveSBiYXRjaCBpbmZvcm1hdGlvbiBhbmQgcmUtc3RvcmUgdGhlIGBjb2xEYXRhKClgLgoKYGBge3IgYWRkLWJhdGNofQojIEFkZCBiYXRjaCBpbmZvcm1hdGlvbgpzYW1wbGVfaW5mbyRiYXRjaCA8LSBjKCJiYXRjaDEiLCAiYmF0Y2gxIiwgImJhdGNoMSIsICJiYXRjaDEiLAogICAgICAgICAgICAgICAgICAgICAgICJiYXRjaDIiLCAiYmF0Y2gxIiwgImJhdGNoMiIsICJiYXRjaDEiKQpgYGAKCklmIHRoaXMgYmF0Y2ggaW5mb3JtYXRpb24gd2VyZSByZWFsIHdlIHdvdWxkIGhhdmUgaW5jbHVkZWQgaXQgd2l0aCB0aGUgc2FtcGxlIG1ldGFkYXRhIHdoZW4gd2UgbWFkZSB0aGUgb3JpZ2luYWwgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3Qgd2l0aCBgdHhpbWV0YWAuCldlIHdvdWxkIHRoZW4gaW5jbHVkZSBpdCBpbiB0aGUgbW9kZWwgc3RvcmVkIGluIG91ciBERVNlcTIgb2JqZWN0IHVzaW5nIHRoZSBgZGVzaWduYCBhcmd1bWVudCAoYGRlc2lnbiA9IH4gdGlzc3VlICsgYmF0Y2hgKSBhbmQgd2Ugd291bGQgcmUtcnVuIHRoZSBgREVTZXFEYXRhU2V0KClgIGFuZCBgdnN0KClgIHN0ZXBzIHdlIGRpZCBhYm92ZS4KSGVyZSB3ZSB3aWxsIHRha2UgYSBiaXQgb2YgYSBzaG9ydGN1dCBhbmQgYWRkIGl0IGRpcmVjdGx5IHRvIHRoZSBgY29sRGF0YSgpYCBmb3Igb3VyIGB2c3QoKWAtdHJhbnNmb3JtZWQgZGF0YS4KCmBgYHtyIGNvbGRhdGEtdnN0LCBsaXZlID0gVFJVRX0KIyBBZGQgdXBkYXRlZCBjb2xEYXRhKCkgd2l0aCBiYXRjaCBpbmZvIHRvIHZzdF9kYXRhCmNvbERhdGEodnN0X2RhdGEpIDwtIHNhbXBsZV9pbmZvCmBgYAoKYGBge3IgcGxvdFBDQS0yLCBsaXZlID0gVFJVRX0KIyBQQ0EgcGxvdCAtIHRpc3N1ZSAqYW5kKiBiYXRjaAojIFdlIHdhbnQgcGxvdFBDQSB0byByZXR1cm4gdGhlIGRhdGEgc28gd2UgY2FuIGhhdmUgbW9yZSBjb250cm9sIGFib3V0IHRoZSBwbG90CnBjYV9kYXRhIDwtIHBsb3RQQ0EoCiAgdnN0X2RhdGEsCiAgaW50Z3JvdXAgPSBjKCJ0aXNzdWUiLCAiYmF0Y2giKSwKICByZXR1cm5EYXRhID0gVFJVRQopCmBgYAoKYGBge3IgcGVyY2VudF92YXJ9CiMgSGVyZSB3ZSBhcmUgc2V0dGluZyB1cCB0aGUgcGVyY2VudCB2YXJpYW5jZSB0aGF0IHdlIGFyZSBleHRyYWN0aW5nIGZyb20gdGhlIGBwY2FfZGF0YWAgb2JqZWN0CnBlcmNlbnRfdmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhX2RhdGEsICJwZXJjZW50VmFyIikpCmBgYAoKTGV0J3MgdXNlIGdncGxvdCB0byB2aXN1YWxpemUgdGhlIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cy4KCmBgYHtyIGNvbG9yLWJ5LWJhdGNoLCBsaXZlID0gVFJVRX0KIyBDb2xvciBwb2ludHMgYnkgImJhdGNoIiBhbmQgdXNlIHNoYXBlIHRvIGluZGljYXRlIHRoZSB0aXNzdWUgb2Ygb3JpZ2luCmdncGxvdDI6OmdncGxvdChwY2FfZGF0YSwKICBnZ3Bsb3QyOjphZXMoCiAgICB4ID0gUEMxLCAKICAgIHkgPSBQQzIsCiAgICBjb2xvciA9IGJhdGNoLAogICAgc2hhcGUgPSB0aXNzdWUKICApCikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZ3Bsb3QyOjp4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50X3ZhclsxXSwgIiUgdmFyaWFuY2UiKSkgKwogIGdncGxvdDI6OnlsYWIocGFzdGUwKCJQQzI6ICIsIHBlcmNlbnRfdmFyWzJdLCAiJSB2YXJpYW5jZSIpKSArCiAgZ2dwbG90Mjo6Y29vcmRfZml4ZWQoKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKUmVjb3JkIHNlc3Npb24gaW5mbyBmb3IgcmVwcm9kdWNpYmlsaXR5ICYgcHJvdmVuYW5jZSBwdXJwb3Nlcy4KCmBgYHtyIHNlc3Npb25pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=
diff --git a/RNA-seq/04-nb_cell_line_tximeta.nb.html b/RNA-seq/04-nb_cell_line_tximeta.nb.html
index e8aa4d0f..5c57acb5 100644
--- a/RNA-seq/04-nb_cell_line_tximeta.nb.html
+++ b/RNA-seq/04-nb_cell_line_tximeta.nb.html
@@ -2916,12 +2916,12 @@ 2021
data. The quant.sf
files for each sample can be found in
data/NB-cell/salmon_quant/<SAMPLE>
.
Save the tximeta
output as
-data/NB-cell/txi/NB-cell_tximeta.RDS
. Note that
+data/NB-cell/txi/NB-cell_tximeta.rds
. Note that
data/NB-cell/txi/
is a new directory.
-LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBhIE5ldXJvYmxhc3RvbWEgY2VsbCBsaW5lIGRhdGEgc2V0IgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgYmUgd29ya2luZyB3aXRoIFJOQS1zZXEgZGF0YSBmcm9tIG5ldXJvYmxhc3RvbWEgKE5CKSBjZWxsIGxpbmVzIGZyb20KW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykKClRoZSBjb3Vyc2UgZGlyZWN0b3JzIGhhdmUgYWxyZWFkeSBwcm9jZXNzZWQgdGhlIHJhdyBkYXRhIHVzaW5nIGBzYWxtb24gcXVhbnRgIGFuZCB0aGUgYHF1YW50LnNmYCBmaWxlcyBmb3IgZWFjaCBzYW1wbGUgY2FuIGJlIGZvdW5kIGluIGBkYXRhL05CLWNlbGwvc2FsbW9uX3F1YW50LzxTQU1QTEU+YC4KCiFbXShkaWFncmFtcy9ybmEtc2VxXzUucG5nKQoKSW4gdGhlIGdhc3RyaWMgY2FuY2VyIGV4YW1wbGUsIHdlIGltcG9ydGVkIFNhbG1vbi1wcm9jZXNzZWQgZGF0YSB3aXRoIGB0eGltZXRhYCB0byB0aGVuIHVzZSB3aXRoIGBERVNlcTJgLgpXZSB3aWxsIGFsc28gdXNlIGBERVNlcTJgIGZvciB0aGVzZSBhbmFseXNlcywgc3BlY2lmaWNhbGx5IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkluIG9yZGVyIHRvIHByZXBhcmUgdGhlIE5CIGNlbGwgbGluZSBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBtb2RpZnkgdGhlIGdhc3RyaWMgY2FuY2VyIHR4aW1ldGEgbm90ZWJvb2sgKGAwMi1nYXN0cmljX2NhbmNlcl90eGltZXRhLWxpdmUuUm1kYCkgYW5kIHNhdmUgdGhpcyBuZXcgbm90ZWJvb2sgYXMgYG5iX2NlbGxfdHhpbWV0YS5SbWRgOgoKKiBUbyBjcmVhdGUgYSBuZXcgbm90ZWJvb2ssIHNlbGVjdCBgRmlsZWAgPiBgTmV3IEZpbGVgID4gYFIgTm90ZWJvb2tgLgpUaGUgbmV3IG5vdGVib29rIHNob3VsZCBhcHBlYXIgaW4geW91ciBTb3VyY2UgUGFuZSBpbiBSU3R1ZGlvLgpTYXZlIHRoZSBuZXcgbm90ZWJvb2ssIHVzaW5nIEN0cmwrUyAoQ21kK1Mgb24gTWFjKSBvciBgRmlsZWAgPiBgU2F2ZWAsIGluIHRoZSBgdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxYCBkaXJlY3Rvcnkgd2l0aCB0aGUgbmFtZSBgbmJfY2VsbF9saW5lX3R4aW1ldGEuUm1kYC4KWW91IGNhbiBhZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCiogQWx0ZXIgdGhlIGNvZGUgZnJvbSBgMDItZ2FzdHJpY19jYW5jZXJfdHhpbWV0YS1saXZlLlJtZGAgdG8gdXNlIHRoZSBOQiBjZWxsIGxpbmUgZGF0YS4KVGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGVhY2ggc2FtcGxlIGNhbiBiZSBmb3VuZCBpbiBgZGF0YS9OQi1jZWxsL3NhbG1vbl9xdWFudC88U0FNUExFPmAuCgoqIFNhdmUgdGhlIGB0eGltZXRhYCBvdXRwdXQgYXMgYGRhdGEvTkItY2VsbC90eGkvTkItY2VsbF90eGltZXRhLlJEU2AuIE5vdGUgdGhhdCBgZGF0YS9OQi1jZWxsL3R4aS9gIGlzIGEgbmV3IGRpcmVjdG9yeS4K
+LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBhIE5ldXJvYmxhc3RvbWEgY2VsbCBsaW5lIGRhdGEgc2V0IgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgYmUgd29ya2luZyB3aXRoIFJOQS1zZXEgZGF0YSBmcm9tIG5ldXJvYmxhc3RvbWEgKE5CKSBjZWxsIGxpbmVzIGZyb20KW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykKClRoZSBjb3Vyc2UgZGlyZWN0b3JzIGhhdmUgYWxyZWFkeSBwcm9jZXNzZWQgdGhlIHJhdyBkYXRhIHVzaW5nIGBzYWxtb24gcXVhbnRgIGFuZCB0aGUgYHF1YW50LnNmYCBmaWxlcyBmb3IgZWFjaCBzYW1wbGUgY2FuIGJlIGZvdW5kIGluIGBkYXRhL05CLWNlbGwvc2FsbW9uX3F1YW50LzxTQU1QTEU+YC4KCiFbXShkaWFncmFtcy9ybmEtc2VxXzUucG5nKQoKSW4gdGhlIGdhc3RyaWMgY2FuY2VyIGV4YW1wbGUsIHdlIGltcG9ydGVkIFNhbG1vbi1wcm9jZXNzZWQgZGF0YSB3aXRoIGB0eGltZXRhYCB0byB0aGVuIHVzZSB3aXRoIGBERVNlcTJgLgpXZSB3aWxsIGFsc28gdXNlIGBERVNlcTJgIGZvciB0aGVzZSBhbmFseXNlcywgc3BlY2lmaWNhbGx5IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkluIG9yZGVyIHRvIHByZXBhcmUgdGhlIE5CIGNlbGwgbGluZSBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBtb2RpZnkgdGhlIGdhc3RyaWMgY2FuY2VyIHR4aW1ldGEgbm90ZWJvb2sgKGAwMi1nYXN0cmljX2NhbmNlcl90eGltZXRhLWxpdmUuUm1kYCkgYW5kIHNhdmUgdGhpcyBuZXcgbm90ZWJvb2sgYXMgYG5iX2NlbGxfdHhpbWV0YS5SbWRgOgoKKiBUbyBjcmVhdGUgYSBuZXcgbm90ZWJvb2ssIHNlbGVjdCBgRmlsZWAgPiBgTmV3IEZpbGVgID4gYFIgTm90ZWJvb2tgLgpUaGUgbmV3IG5vdGVib29rIHNob3VsZCBhcHBlYXIgaW4geW91ciBTb3VyY2UgUGFuZSBpbiBSU3R1ZGlvLgpTYXZlIHRoZSBuZXcgbm90ZWJvb2ssIHVzaW5nIEN0cmwrUyAoQ21kK1Mgb24gTWFjKSBvciBgRmlsZWAgPiBgU2F2ZWAsIGluIHRoZSBgdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxYCBkaXJlY3Rvcnkgd2l0aCB0aGUgbmFtZSBgbmJfY2VsbF9saW5lX3R4aW1ldGEuUm1kYC4KWW91IGNhbiBhZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCiogQWx0ZXIgdGhlIGNvZGUgZnJvbSBgMDItZ2FzdHJpY19jYW5jZXJfdHhpbWV0YS1saXZlLlJtZGAgdG8gdXNlIHRoZSBOQiBjZWxsIGxpbmUgZGF0YS4KVGhlIGBxdWFudC5zZmAgZmlsZXMgZm9yIGVhY2ggc2FtcGxlIGNhbiBiZSBmb3VuZCBpbiBgZGF0YS9OQi1jZWxsL3NhbG1vbl9xdWFudC88U0FNUExFPmAuCgoqIFNhdmUgdGhlIGB0eGltZXRhYCBvdXRwdXQgYXMgYGRhdGEvTkItY2VsbC90eGkvTkItY2VsbF90eGltZXRhLnJkc2AuIE5vdGUgdGhhdCBgZGF0YS9OQi1jZWxsL3R4aS9gIGlzIGEgbmV3IGRpcmVjdG9yeS4K
diff --git a/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd b/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd
index 5e9afa62..73b470b6 100644
--- a/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd
+++ b/RNA-seq/05-nb_cell_line_DESeq2-live.Rmd
@@ -50,7 +50,7 @@ library(EnhancedVolcano)
```{r input-files}
# directory with the tximeta processed data
txi_dir <- file.path("data", "NB-cell", "txi")
-txi_file <- file.path(txi_dir, "NB-cell_tximeta.RDS")
+txi_file <- file.path(txi_dir, "NB-cell_tximeta.rds")
```
@@ -61,9 +61,7 @@ We'll create a results directory to hold our results.
```{r results-dir}
# Create a results directory if it doesn't already exist
results_dir <- file.path("results", "NB-cell")
-if (!dir.exists(results_dir)) {
- dir.create(results_dir, recursive = TRUE)
-}
+fs::dir_create(results_dir)
```
We will also need a directory to store our plots.
@@ -77,7 +75,7 @@ We will also need a directory to store our plots.
```{r output-files}
# RDS for the output of DESeq analysis
deseq_file <- file.path(results_dir,
- "NB-cell_DESeq_amplified_v_nonamplified.RDS")
+ "NB-cell_DESeq_amplified_v_nonamplified.rds")
# DESeq2 results table
deseq_df_file <- file.path(results_dir,
@@ -151,8 +149,11 @@ Genes that have very low counts are not likely to yield reliable differential ex
We will keep only genes with total counts of at least 10 across all samples.
```{r filter_ddset}
+# create a vector of TRUE and FALSE values where
+# TRUE corresponds to genes with counts of at least 10
genes_to_keep <- rowSums(counts(ddset)) >= 10
-ddset <- ddset[genes_to_keep, ]
+# use which() to prevent any NAs sneaking through
+ddset <- ddset[which(genes_to_keep), ]
```
@@ -210,11 +211,9 @@ So for this data set we will be using `ashr` ([Stephens 2017](https://doi.org/10
```{r lfc_shrink}
# calculate shrunken log2 fold change estimates
deseq_shrunken <- lfcShrink(deseq_object,
- # the coefficient we want to reestimate
- coef = 2,
- # We will use `ashr` for estimation
- type = "ashr"
- )
+ coef = 2, # the coefficient we want to reestimate
+ type = "ashr" # We will use `ashr` for estimation
+)
```
Let's compare the log2 fold change estimates from the two results tables by creating a plot.
@@ -226,20 +225,23 @@ comparison_df <- data.frame(
lfc_original = deseq_results$log2FoldChange,
lfc_shrunken = deseq_shrunken$log2FoldChange,
logmean = log10(deseq_results$baseMean)
- )
+)
```
Now we can plot the original and shrunken log2 fold change values to see what happened after shrinkage.
```{r plot_comparison}
ggplot(comparison_df,
- aes(x = lfc_original,
- y = lfc_shrunken,
- color = logmean)) +
+ aes(
+ x = lfc_original,
+ y = lfc_shrunken,
+ color = logmean
+ )
+) +
geom_point(alpha = 0.1) +
theme_bw() +
scale_color_viridis_c() +
- coord_cartesian(xlim = c(-10,10), ylim = c(-10,10)) # zoom in on the middle
+ coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10)) # zoom in on the middle
```
We will now do a bit of manipulation to store the results in a data frame and add the gene symbols.
@@ -279,16 +281,16 @@ Even better, it outputs a `ggplot2` object, so if we want to customize it furthe
```{r volcano}
EnhancedVolcano(deseq_df,
- x = 'log2FoldChange', # fold change statistic to plot
- y = 'pvalue', # significance values
- lab = deseq_df$gene_symbol, # labels for points
- pCutoff = 1e-05, # The p value cutoff we will use (default)
- FCcutoff = 1, # The fold change cutoff (default)
- title = NULL, # no title
- subtitle = NULL, # or subtitle
- caption = NULL, # or caption
- labSize = 3 # smaller labels
- ) +
+ x = "log2FoldChange", # fold change statistic to plot
+ y = "pvalue", # significance values
+ lab = deseq_df$gene_symbol, # labels for points
+ pCutoff = 1e-05, # The p value cutoff we will use (default)
+ FCcutoff = 1, # The fold change cutoff (default)
+ title = NULL, # no title
+ subtitle = NULL, # or subtitle
+ caption = NULL, # or caption
+ labSize = 3 # smaller labels
+) +
# change the overall theme
theme_classic() +
# move the legend to the bottom
diff --git a/RNA-seq/05-nb_cell_line_DESeq2.nb.html b/RNA-seq/05-nb_cell_line_DESeq2.nb.html
index 75271f50..69b713f5 100644
--- a/RNA-seq/05-nb_cell_line_DESeq2.nb.html
+++ b/RNA-seq/05-nb_cell_line_DESeq2.nb.html
@@ -3056,10 +3056,10 @@ Directories and files
Input
-
+
# directory with the tximeta processed data
txi_dir <- file.path("data", "NB-cell", "txi")
-txi_file <- file.path(txi_dir, "NB-cell_tximeta.RDS")
+txi_file <- file.path(txi_dir, "NB-cell_tximeta.rds")
@@ -3067,33 +3067,29 @@ We’ll create a results directory to hold our results.
- +# Create a results directory if it doesn't already exist
results_dir <- file.path("results", "NB-cell")
-if (!dir.exists(results_dir)) {
- dir.create(results_dir, recursive = TRUE)
-}
+fs::dir_create(results_dir)
We will also need a directory to store our plots.
- +# Create a plots directory if it doesn't already exist
plots_dir <- file.path("plots", "NB-cell")
-if (!dir.exists(plots_dir)) {
- dir.create(plots_dir, recursive = TRUE)
-}
+fs::dir_create(plots_dir)
-
+
# RDS for the output of DESeq analysis
deseq_file <- file.path(results_dir,
- "NB-cell_DESeq_amplified_v_nonamplified.RDS")
+ "NB-cell_DESeq_amplified_v_nonamplified.rds")
# DESeq2 results table
deseq_df_file <- file.path(results_dir,
@@ -3243,11 +3239,11 @@ Preparation
DESeq Dataset creation
-
+
# Create a DESeq2 dataset from `gene_summarized`
# remember that `status` is the variable of interest here
ddset <- DESeqDataSet(gene_summarized,
- design = ~ status)
+ design = ~status)
using counts and average transcript lengths from tximeta
@@ -3267,9 +3263,12 @@ genes_to_keep <- rowSums(counts(ddset)) >= 10
-ddset <- ddset[genes_to_keep, ]
+
+# create a vector of TRUE and FALSE values where
+# TRUE corresponds to genes with counts of at least 10
+genes_to_keep <- rowSums(counts(ddset)) >= 10
+# use which() to prevent any NAs sneaking through
+ddset <- ddset[which(genes_to_keep), ]
@@ -3429,14 +3428,12 @@ # calculate shrunken log2 fold change estimates
deseq_shrunken <- lfcShrink(deseq_object,
- # the coefficient we want to reestimate
- coef = 2,
- # We will use `ashr` for estimation
- type = "ashr"
- )
+ coef = 2, # the coefficient we want to reestimate
+ type = "ashr" # We will use `ashr` for estimation
+)
using 'ashr' for LFC shrinkage. If used in published research, please cite:
@@ -3450,12 +3447,12 @@ Shrinking log2 fold change estimates
First we will combine the results into a new data frame.
-
+
comparison_df <- data.frame(
lfc_original = deseq_results$log2FoldChange,
lfc_shrunken = deseq_shrunken$log2FoldChange,
logmean = log10(deseq_results$baseMean)
- )
+)
@@ -3463,15 +3460,18 @@ ggplot(comparison_df,
- aes(x = lfc_original,
- y = lfc_shrunken,
- color = logmean)) +
+ aes(
+ x = lfc_original,
+ y = lfc_shrunken,
+ color = logmean
+ )
+) +
geom_point(alpha = 0.1) +
theme_bw() +
scale_color_viridis_c() +
- coord_cartesian(xlim = c(-10,10), ylim = c(-10,10)) # zoom in on the middle
+ coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10)) # zoom in on the middle
@@ -3535,18 +3535,18 @@ EnhancedVolcano(deseq_df,
- x = 'log2FoldChange', # fold change statistic to plot
- y = 'pvalue', # significance values
- lab = deseq_df$gene_symbol, # labels for points
- pCutoff = 1e-05, # The p value cutoff we will use (default)
- FCcutoff = 1, # The fold change cutoff (default)
- title = NULL, # no title
- subtitle = NULL, # or subtitle
- caption = NULL, # or caption
- labSize = 3 # smaller labels
- ) +
+ x = "log2FoldChange", # fold change statistic to plot
+ y = "pvalue", # significance values
+ lab = deseq_df$gene_symbol, # labels for points
+ pCutoff = 1e-05, # The p value cutoff we will use (default)
+ FCcutoff = 1, # The fold change cutoff (default)
+ title = NULL, # no title
+ subtitle = NULL, # or subtitle
+ caption = NULL, # or caption
+ labSize = 3 # smaller labels
+) +
# change the overall theme
theme_classic() +
# move the legend to the bottom
@@ -3578,8 +3578,8 @@ Session Info
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3615,16 +3615,16 @@ Session Info
[1] tidyselect_1.2.1 viridisLite_0.4.2 dplyr_1.1.4
[4] farver_2.1.1 fastmap_1.1.1 digest_0.6.35
[7] lifecycle_1.0.4 invgamma_1.1 magrittr_2.0.3
-[10] compiler_4.4.0 rlang_1.1.3 sass_0.4.9
-[13] tools_4.4.0 utf8_1.2.4 yaml_2.3.8
+[10] compiler_4.4.1 rlang_1.1.3 sass_0.4.9
+[13] tools_4.4.1 utf8_1.2.4 yaml_2.3.8
[16] knitr_1.46 S4Arrays_1.4.0 labeling_0.4.3
[19] bit_4.0.5 DelayedArray_0.30.0 abind_1.4-5
-[22] BiocParallel_1.38.0 withr_3.0.0 grid_4.4.0
+[22] BiocParallel_1.38.0 withr_3.0.0 grid_4.4.1
[25] fansi_1.0.6 colorspace_2.1-0 scales_1.3.0
[28] cli_3.6.2 rmarkdown_2.26 crayon_1.5.2
[31] ragg_1.3.0 generics_0.1.3 httr_1.4.7
[34] tzdb_0.4.0 getopt_1.20.4 cachem_1.0.8
-[37] stringr_1.5.1 zlibbioc_1.50.0 parallel_4.4.0
+[37] stringr_1.5.1 zlibbioc_1.50.0 parallel_4.4.1
[40] XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0
[43] jsonlite_1.8.8 hms_1.1.3 mixsqp_0.3-54
[46] bit64_4.0.5 irlba_2.3.5.1 systemfonts_1.0.6
@@ -3636,12 +3636,13 @@ Session Info
[64] vroom_1.6.5 evaluate_0.23 lattice_0.22-6
[67] readr_2.1.5 highr_0.10 SQUAREM_2021.1
[70] ashr_2.2-63 bslib_0.7.0 Rcpp_1.0.12
-[73] SparseArray_1.4.0 xfun_0.43 pkgconfig_2.0.3
+[73] SparseArray_1.4.0 xfun_0.43 fs_1.6.4
+[76] pkgconfig_2.0.3
-LS0tCnRpdGxlOiAiTmV1cm9ibGFzdG9tYSBDZWxsIExpbmU6IERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpdGggREVTZXEyIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2l0aCBgREVTZXEyYAotIEFwcGx5IGEgc2hyaW5rYWdlIGFsZ29yaXRobSB0byBpbXByb3ZlIGVzdGltYXRlcyBvZiBleHByZXNzaW9uIGNoYW5nZXMKLSBEcmF3IGEgdm9sY2FubyBwbG90IHdpdGggdGhlIGBFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgcGVyZm9ybSBhbiBhbmFseXNpcyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIF9NWUNOXyBhbXBsaWZpZWQgdnMuIG5vbmFtcGxpZmllZCBuZXVyb2JsYXN0b21hIGNlbGwgbGluZXMuCgpUaGVzZSBSTkEtc2VxIGRhdGEgYXJlIGZyb20gW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykuCgpNb3JlIGluZm9ybWF0aW9uIGFib3V0IERFU2VxMiBjYW4gYmUgZm91bmQgaW4gdGhlIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgpERVNlcTIgdGFrZXMgdW5ub3JtYWxpemVkIGNvdW50cyBvciBlc3RpbWF0ZWQgY291bnRzIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoqIFtFc3RpbWF0ZXMgc2l6ZSBmYWN0b3JzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvZXN0aW1hdGVTaXplRmFjdG9ycykKKiBbRXN0aW1hdGVzIGRpc3BlcnNpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ERVNlcTIvdmVyc2lvbnMvMS4xMi4zL3RvcGljcy9lc3RpbWF0ZURpc3BlcnNpb25zKQoqIE5lZ2F0aXZlIGJpbm9taWFsIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCBmaXR0aW5nIGFuZCBbV2FsZCBzdGF0aXN0aWNzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvbmJpbm9tV2FsZFRlc3QpCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCiMjIExpYnJhcmllcyBhbmQgZnVuY3Rpb25zCgpgYGB7ciBsaWJyYXJ5fQojIExvYWQgdGhlIERFU2VxMiBsaWJyYXJ5CmxpYnJhcnkoREVTZXEyKQoKIyBXZSB3aWxsIGJlIG1ha2luZyBmYW5jeSB2b2xjYW5vIHBsb3RzCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKKipJbnB1dCoqCgpgYGB7ciBpbnB1dC1maWxlc30KIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJOQi1jZWxsIiwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiTkItY2VsbF90eGltZXRhLlJEUyIpCmBgYAoKCioqT3V0cHV0KioKCldlJ2xsIGNyZWF0ZSBhIHJlc3VsdHMgZGlyZWN0b3J5IHRvIGhvbGQgb3VyIHJlc3VsdHMuCgpgYGB7ciByZXN1bHRzLWRpcn0KIyBDcmVhdGUgYSByZXN1bHRzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIk5CLWNlbGwiKQppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCldlIHdpbGwgYWxzbyBuZWVkIGEgZGlyZWN0b3J5IHRvIHN0b3JlIG91ciBwbG90cy4KCmBgYHtyIHBsb3RzLWRpciwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgcGxvdHMgZGlyZWN0b3J5IGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdApwbG90c19kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICJOQi1jZWxsIikKaWYgKCFkaXIuZXhpc3RzKHBsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHBsb3RzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCgpgYGB7ciBvdXRwdXQtZmlsZXN9CiMgUkRTIGZvciB0aGUgb3V0cHV0IG9mIERFU2VxIGFuYWx5c2lzCmRlc2VxX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAiTkItY2VsbF9ERVNlcV9hbXBsaWZpZWRfdl9ub25hbXBsaWZpZWQuUkRTIikKCiMgREVTZXEyIHJlc3VsdHMgdGFibGUKZGVzZXFfZGZfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQi1jZWxsX0RFU2VxX2FtcGxpZmllZF92X25vbmFtcGxpZmllZF9yZXN1bHRzLnRzdiIpCgojIFBORyBvZiB0aGUgdm9sY2FubyBwbG90CnZvbGNhbm9fZmlsZSA8LSBmaWxlLnBhdGgocGxvdHNfZGlyLCAiTkItY2VsbF92b2xjYW5vLnBuZyIpCmBgYAoKIyMgREVTZXEyCgojIyMgQ3JlYXRpbmcgYSBERVNlcTIgZGF0YXNldCBmcm9tIHR4aW1ldGEgb2JqZWN0CgpGaXJzdCwgbGV0J3MgcmVhZCBpbiB0aGUgZGF0YSB3ZSBwcm9jZXNzZWQgd2l0aCBgdHhpbWV0YWAuCgojIyMjIFByZXBhcmF0aW9uCgpgYGB7ciByZWFkX3JkcywgbGl2ZSA9IFRSVUV9CiMgUmVhZCBpbiB0aGUgUkRTIGZpbGUgd2UgY3JlYXRlZCBpbiB0aGUgbGFzdCBub3RlYm9vawpnZW5lX3N1bW1hcml6ZWQgPC0gcmVhZHI6OnJlYWRfcmRzKHR4aV9maWxlKQpgYGAKCldlJ3JlIG1vc3QgaW50ZXJlc3RlZCBpbiBfTVlDTl8gYW1wbGlmaWNhdGlvbiwgd2hpY2ggd2UgaGFkIHN0b3JlZCBpbiB0aGUgYHN0YXR1c2AgY29sdW1uIG9mIHRoZSBzYW1wbGUgbWV0YWRhdGEgb2YgYGdlbmVfc3VtbWFyaXplZGAuCldoaWxlIHRoZSBzYW1wbGUgbWV0YWRhdGEgaXMgc3RvcmVkIGludGVybmFsbHkgaW4gdGhlIGBjb2xEYXRhYCBzbG90LCB0aGUgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3QgbWFrZXMgaXQgZWFzeSBmb3IgdXMgdG8gYWNjZXNzIGl0IGFzIGlmIGl0IHdlcmUganVzdCBhIGNvbHVtbiBvZiBhIGRhdGEgZnJhbWUsIHVzaW5nIHRoZSBmYW1pbGlhciBgJGAgc3ludGF4LgoKCmBgYHtyIFN0YXR1cywgbGl2ZSA9IFRSVUV9CmdlbmVfc3VtbWFyaXplZCRzdGF0dXMKYGBgCgpUaGlzIGlzIHN0b3JlZCBhcyBhIGBjaGFyYWN0ZXJgIHR5cGUsIGJ1dCB0byBnaXZlIGEgYml0IG1vcmUgaW5mb3JtYXRpb24gdG8gYERFU2VxYCwgd2Ugd2lsbCBjb252ZXJ0IHRoaXMgdG8gYSBgZmFjdG9yYC4KCmBgYHtyIHN0YXR1c19mYWN0b3IsIGxpdmUgPSBUUlVFfQpnZW5lX3N1bW1hcml6ZWQkc3RhdHVzIDwtIGFzLmZhY3RvcihnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCldlJ2xsIHdhbnQgdG8gdXNlIHRoZSAiTm9uYW1wbGlmaWVkIiBzYW1wbGVzIGFzIG91ciBfcmVmZXJlbmNlXy4KTGV0J3MgbG9vayBhdCB0aGUgYGxldmVsc2Agb2YgYHN0YXR1c2AuCgpgYGB7ciBsZXZlbHN9CmxldmVscyhnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGVzZSBhcmUgaW4gYWxwaGFiZXRpY2FsIG9yZGVyLCBzbyAiQW1wbGlmaWVkIiBzYW1wbGVzIHdvdWxkIGJlIHRoZSByZWZlcmVuY2UuCldlIGNhbiB1c2UgdGhlIGByZWxldmVsKClgIGZ1bmN0aW9uIHRvIHJlbWVkeSB0aGlzLgoKYGBge3IgcmVsZXZlbH0KZ2VuZV9zdW1tYXJpemVkJHN0YXR1cyA8LSByZWxldmVsKGdlbmVfc3VtbWFyaXplZCRzdGF0dXMsIHJlZiA9ICJOb25hbXBsaWZpZWQiKQpgYGAKCmBgYHtyIGNoZWNrLWxldmVscywgbGl2ZSA9IFRSVUV9CiMgQ2hlY2sgd2hhdCB0aGUgbGV2ZWxzIGFyZSBub3cKbGV2ZWxzKGdlbmVfc3VtbWFyaXplZCRzdGF0dXMpCmBgYAoKCgojIyMjIERFU2VxIERhdGFzZXQgY3JlYXRpb24KCmBgYHtyIGRkc2V0LCBsaXZlID0gVFJVRX0KIyBDcmVhdGUgYSBERVNlcTIgZGF0YXNldCBmcm9tIGBnZW5lX3N1bW1hcml6ZWRgCiMgcmVtZW1iZXIgdGhhdCBgc3RhdHVzYCBpcyB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QgaGVyZQpkZHNldCA8LSBERVNlcURhdGFTZXQoZ2VuZV9zdW1tYXJpemVkLAogICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBzdGF0dXMpCmBgYAoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCiMjIyBGaWx0ZXJpbmcgbG93LWV4cHJlc3NlZCBnZW5lcwoKR2VuZXMgdGhhdCBoYXZlIHZlcnkgbG93IGNvdW50cyBhcmUgbm90IGxpa2VseSB0byB5aWVsZCByZWxpYWJsZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzLCBzbyB3ZSB3aWxsIGRvIHNvbWUgbGlnaHQgW3ByZS1maWx0ZXJpbmddKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3ByZS1maWx0ZXJpbmcpLgpXZSB3aWxsIGtlZXAgb25seSBnZW5lcyB3aXRoIHRvdGFsIGNvdW50cyBvZiBhdCBsZWFzdCAxMCBhY3Jvc3MgYWxsIHNhbXBsZXMuCgpgYGB7ciBmaWx0ZXJfZGRzZXR9CmdlbmVzX3RvX2tlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzZXQpKSA+PSAxMApkZHNldCA8LSBkZHNldFtnZW5lc190b19rZWVwLCBdCmBgYAoKCiMjIyBUaGUgYERFU2VxKClgIGZ1bmN0aW9uCgpXZSdsbCBub3cgdXNlIHRoZSB3cmFwcGVyIGZ1bmN0aW9uIGBERVNlcSgpYCB0byBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KQXMgbWVudGlvbmVkIGVhcmxpZXIsIHRoaXMgcGVyZm9ybXMgYSBudW1iZXIgb2Ygc3RlcHMsIGluY2x1ZGluZyBhbiBbb3V0bGllciByZW1vdmFsIHByb2NlZHVyZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2FwcHJvYWNoLXRvLWNvdW50LW91dGxpZXJzKS4KRm9yIHRoaXMgcGFydGljdWxhciBkYXRhc2V0LCB0aGVyZSBpcyBhIHByZXR0eSBsYXJnZSBudW1iZXIgb2Ygb3V0bGllcnMsIHdoaWNoIGNhbiBiZSBhIGJpdCBvZiBhIHJlZCBmbGFnLCBidXQgd2Ugd2lsbCBwcm9jZWVkIGZvciBub3cuCgpgYGB7ciBERVNlcX0KZGVzZXFfb2JqZWN0IDwtIERFU2VxKGRkc2V0KQpgYGAKCkxldCdzIHNhdmUgdGhpcyB0byBvdXIgcmVzdWx0cyBmaWxlLgoKYGBge3Igd3JpdGVfcmRzLCBsaXZlID0gVFJVRX0KIyBTYXZlIHRoZSByZXN1bHRzIGFzIGFuIFJEUwpyZWFkcjo6d3JpdGVfcmRzKGRlc2VxX29iamVjdCwgZmlsZSA9IGRlc2VxX2ZpbGUpCmBgYAoKTm93IHdlIHdpbGwgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdHMgdGFibGUuCgpgYGB7ciBkZXNlcV9yZXN1bHRzfQpkZXNlcV9yZXN1bHRzIDwtIHJlc3VsdHMoZGVzZXFfb2JqZWN0KQpkZXNlcV9yZXN1bHRzCmBgYAoKSG93IG1hbnkgZ2VuZXMgd2VyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgKEZEUiA8IDAuMDUpPwoKYGBge3IgcmVzdWx0c19zdW1tYXJ5fQpzdW1tYXJ5KGRlc2VxX3Jlc3VsdHMsIGFscGhhID0gMC4wNSkKYGBgCgoKIyMjIFNocmlua2luZyBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlcwoKVGhlIGVzdGltYXRlcyBvZiBsb2cyIGZvbGQgY2hhbmdlIGNhbGN1bGF0ZWQgYnkgYERFU2VxKClgIGFyZSBub3QgY29ycmVjdGVkIGZvciBleHByZXNzaW9uIGxldmVsLgpUaGlzIG1lYW5zIHRoYXQgd2hlbiBjb3VudHMgYXJlIHNtYWxsLCB3ZSBhcmUgbGlrZWx5IHRvIGVuZCB1cCB3aXRoIHNvbWUgbGFyZ2UgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgb3ZlcmVzdGltYXRlIHRoZSB0cnVlIGV4dGVudCBvZiB0aGUgY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucy4KCldlIGNhbiBjb3JyZWN0IHRoaXMgYnkgYXBwbHlpbmcgYSAic2hyaW5rYWdlIiBwcm9jZWR1cmUsIHdoaWNoIHdpbGwgYWRqdXN0IGxhcmdlIHZhbHVlcyB3aXRoIHNtYWxsIGNvdW50cyBkb3dud2FyZCwgd2hpbGUgcHJlc2VydmluZyB2YWx1ZXMgd2l0aCBsYXJnZXIgY291bnRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIG1vcmUgYWNjdXJhdGUuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGxmY1NocmluaygpYCBmdW5jdGlvbiwgYnV0IGZpcnN0IHdlIG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQvb3IgcG9zaXRpb24gb2YgdGhlICJjb2VmZmljaWVudCIgdGhhdCB3YXMgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIHRoZSBgcmVzdWx0c05hbWVzKClgIGZ1bmN0aW9uCgpgYGB7ciBkZXNlcV9jb2VmfQojIGdldCB0aGUgZGVzZXEgY29lZmZpY2llbnQgbmFtZXM6CnJlc3VsdHNOYW1lcyhkZXNlcV9vYmplY3QpCmBgYAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGBzdGF0dXNgIGNvZWZmaWNpZW50LCB3aGljaCBpcyBpbiBwb3NpdGlvbiAyLgoKVGhlcmUgYXJlIGEgZmV3IG9wdGlvbnMgZm9yIHRoZSBzaHJpbmthZ2UgZXN0aW1hdGlvbi4KVGhlIGRlZmF1bHQgaXMgYGFwZWdsbWAgKFtaaHUgX2V0IGFsLl8gMjAxOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnR5ODk1KSksIGJ1dCB3ZSBoYXZlIGZvdW5kIHRoYXQgdGhpcyBjYW4gYmUgc2Vuc2l0aXZlIHRvIGV4dHJlbWUgb3V0bGllcnMsIHdoaWNoIGFyZSBkZWZpbml0ZWx5IGEgZmFjdG9yIGluIHRoaXMgZGF0YSBzZXQuClNvIGZvciB0aGlzIGRhdGEgc2V0IHdlIHdpbGwgYmUgdXNpbmcgYGFzaHJgIChbU3RlcGhlbnMgMjAxN10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvc3RhdGlzdGljcy9reHcwNDEpKQoKYGBge3IgbGZjX3Nocmlua30KIyBjYWxjdWxhdGUgc2hydW5rZW4gbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMKZGVzZXFfc2hydW5rZW4gPC0gbGZjU2hyaW5rKGRlc2VxX29iamVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIGNvZWZmaWNpZW50IHdlIHdhbnQgdG8gcmVlc3RpbWF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZiA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdlIHdpbGwgdXNlIGBhc2hyYCBmb3IgZXN0aW1hdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJhc2hyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCkxldCdzIGNvbXBhcmUgdGhlIGxvZzIgZm9sZCBjaGFuZ2UgZXN0aW1hdGVzIGZyb20gdGhlIHR3byByZXN1bHRzIHRhYmxlcyBieSBjcmVhdGluZyBhIHBsb3QuCgpGaXJzdCB3ZSB3aWxsIGNvbWJpbmUgdGhlIHJlc3VsdHMgaW50byBhIG5ldyBkYXRhIGZyYW1lLgoKYGBge3IgY29tcGFyZV9zaHJpbmt9CmNvbXBhcmlzb25fZGYgPC0gZGF0YS5mcmFtZSgKICBsZmNfb3JpZ2luYWwgPSBkZXNlcV9yZXN1bHRzJGxvZzJGb2xkQ2hhbmdlLAogIGxmY19zaHJ1bmtlbiA9IGRlc2VxX3NocnVua2VuJGxvZzJGb2xkQ2hhbmdlLAogIGxvZ21lYW4gPSBsb2cxMChkZXNlcV9yZXN1bHRzJGJhc2VNZWFuKQogICkKYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlIG9yaWdpbmFsIGFuZCBzaHJ1bmtlbiBsb2cyIGZvbGQgY2hhbmdlIHZhbHVlcyB0byBzZWUgd2hhdCBoYXBwZW5lZCBhZnRlciBzaHJpbmthZ2UuCgpgYGB7ciBwbG90X2NvbXBhcmlzb259CmdncGxvdChjb21wYXJpc29uX2RmLAogICAgICAgYWVzKHggPSBsZmNfb3JpZ2luYWwsCiAgICAgICAgICAgeSA9IGxmY19zaHJ1bmtlbiwKICAgICAgICAgICBjb2xvciA9IGxvZ21lYW4pKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTEwLDEwKSwgeWxpbSA9IGMoLTEwLDEwKSkgIyB6b29tIGluIG9uIHRoZSBtaWRkbGUKYGBgCgpXZSB3aWxsIG5vdyBkbyBhIGJpdCBvZiBtYW5pcHVsYXRpb24gdG8gc3RvcmUgdGhlIHJlc3VsdHMgaW4gYSBkYXRhIGZyYW1lIGFuZCBhZGQgdGhlIGdlbmUgc3ltYm9scy4KCmBgYHtyIHJlc3VsdHNfZGF0YWZyYW1lfQojIHRoaXMgaXMgb2YgY2xhc3MgREVTZXFSZXN1bHRzIC0tIHdlIHdhbnQgYSBkYXRhIGZyYW1lCmRlc2VxX2RmIDwtIGRlc2VxX3NocnVua2VuIHw+CiAgIyBjb252ZXJ0IHRvIGEgZGF0YSBmcmFtZQogIGFzLmRhdGEuZnJhbWUoKSB8PgogICMgdGhlIGdlbmUgaWRzIHdlcmUgc3RvcmVkIGFzIHJvdyBuYW1lcyAtLSBsZXQncyB0aGVtIGEgY29sdW1uIGZvciBlYXN5IGRpc3BsYXkKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiZ2VuZV9pZCIpIHw+CiAgIyBhZGQgb24gdGhlIGdlbmUgc3ltYm9scyBmcm9tIHRoZSBvcmlnaW5hbCBkZXNlcSBvYmplY3QKICBkcGx5cjo6bXV0YXRlKGdlbmVfc3ltYm9sID0gcm93RGF0YShkZXNlcV9vYmplY3QpJGdlbmVfbmFtZSkKYGBgCgpMZXQncyBwcmludCBvdXQgdGhlIHJlc3VsdHMgdGFibGUsIHNvcnRlZCBieSBsb2cyIGZvbGQgY2hhbmdlLgpUaGUgaGlnaGVzdCB2YWx1ZXMgc2hvdWxkIGJlIGdlbmVzIG1vcmUgZXhwcmVzc2VkIGluIHRoZSBfTVlDTl8gYW1wbGlmaWVkIGNlbGwgbGluZXMuCgpgYGB7ciBzb3J0ZWRfdGFibGUsIGxpdmUgPSBUUlVFfQojIFByaW50IHRoZSB0YWJsZSBzb3J0ZWQgYnkgbG9nMkZvbGRDaGFuZ2UKZGVzZXFfZGYgfD4KICBkcGx5cjo6YXJyYW5nZShkcGx5cjo6ZGVzYyhsb2cyRm9sZENoYW5nZSkpCmBgYAoKTm93IGxldCdzIHdyaXRlIHRoZSBmdWxsIHJlc3VsdHMgdGFibGUgdG8gYSBmaWxlLgoKYGBge3Igd3JpdGVfdHN2fQpyZWFkcjo6d3JpdGVfdHN2KGRlc2VxX2RmLCBmaWxlID0gZGVzZXFfZGZfZmlsZSkKYGBgCgoKIyMgTWFraW5nIGEgVm9sY2FubyBQbG90CgpXaXRoIHRoZXNlIHNocnVua2VuIGVmZmVjdCBzaXplcywgd2Ugd2lsbCBkcmF3IGEgdm9sY2FubyBwbG90LCB1c2luZyB0aGUgW2BFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9rZXZpbmJsaWdoZS9FbmhhbmNlZFZvbGNhbm8pIHRvIG1ha2UgaXQgYSBiaXQgZWFzaWVyLgpUaGlzIHBhY2thZ2UgYXV0b21hdGljYWxseSBjb2xvciBjb2RlcyB0aGUgcG9pbnRzIGJ5IGN1dG9mZnMgZm9yIGJvdGggc2lnbmlmaWNhbmNlIGFuZCBmb2xkIGNoYW5nZSBhbmQgbGFiZWxzIG1hbnkgb2YgdGhlIHNpZ25pZmljYW50IGdlbmVzIChzdWJqZWN0IHRvIHNwYWNpbmcpLgpgRW5oYW5jZWRWb2xjYW5vYCBoYXMgbWFueSwgbWFueSBvcHRpb25zLCB3aGljaCBpcyBhIGdvb2QgdGhpbmcgaWYgeW91IGRvbid0IGxpa2UgYWxsIG9mIGl0J3MgZGVmYXVsdCBzZXR0aW5ncy4KRXZlbiBiZXR0ZXIsIGl0IG91dHB1dHMgYSBgZ2dwbG90MmAgb2JqZWN0LCBzbyBpZiB3ZSB3YW50IHRvIGN1c3RvbWl6ZSBpdCBmdXJ0aGVyLCB3ZSBjYW4gZG8gdGhhdCB3aXRoIHRoZSBzYW1lIGBnZ3Bsb3QyYCBjb21tYW5kcyB3ZSBoYXZlIHVzZWQgYmVmb3JlLgoKYGBge3Igdm9sY2Fub30KRW5oYW5jZWRWb2xjYW5vKGRlc2VxX2RmLAogICAgICAgICAgICAgICAgeCA9ICdsb2cyRm9sZENoYW5nZScsICMgZm9sZCBjaGFuZ2Ugc3RhdGlzdGljIHRvIHBsb3QKICAgICAgICAgICAgICAgIHkgPSAncHZhbHVlJywgIyBzaWduaWZpY2FuY2UgdmFsdWVzCiAgICAgICAgICAgICAgICBsYWIgPSBkZXNlcV9kZiRnZW5lX3N5bWJvbCwgIyBsYWJlbHMgZm9yIHBvaW50cwogICAgICAgICAgICAgICAgcEN1dG9mZiA9IDFlLTA1LCAjIFRoZSBwIHZhbHVlIGN1dG9mZiB3ZSB3aWxsIHVzZSAoZGVmYXVsdCkKICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwgIyBUaGUgZm9sZCBjaGFuZ2UgY3V0b2ZmIChkZWZhdWx0KQogICAgICAgICAgICAgICAgdGl0bGUgPSBOVUxMLCAjIG5vIHRpdGxlCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IE5VTEwsICMgb3Igc3VidGl0bGUKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBOVUxMLCAjIG9yIGNhcHRpb24KICAgICAgICAgICAgICAgIGxhYlNpemUgPSAzICAjIHNtYWxsZXIgbGFiZWxzCiAgICAgICAgICAgICAgICApICsKICAjIGNoYW5nZSB0aGUgb3ZlcmFsbCB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyBtb3ZlIHRoZSBsZWdlbmQgdG8gdGhlIGJvdHRvbQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCldlIHdpbGwgc2F2ZSB0aGlzIHBsb3QgdG8gYSBmaWxlIGFzIHdlbGw6CgpgYGB7ciBzYXZlX3Bsb3R9Cmdnc2F2ZSh2b2xjYW5vX2ZpbGUsIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgoKIyMgU2Vzc2lvbiBJbmZvCgpSZWNvcmQgc2Vzc2lvbiBpbmZvIGZvciByZXByb2R1Y2liaWxpdHkgJiBwcm92ZW5hbmNlIHB1cnBvc2VzLgoKYGBge3Igc2Vzc2lvbmluZm99CnNlc3Npb25JbmZvKCkKYGBgCg==
+LS0tCnRpdGxlOiAiTmV1cm9ibGFzdG9tYSBDZWxsIExpbmU6IERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpdGggREVTZXEyIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2l0aCBgREVTZXEyYAotIEFwcGx5IGEgc2hyaW5rYWdlIGFsZ29yaXRobSB0byBpbXByb3ZlIGVzdGltYXRlcyBvZiBleHByZXNzaW9uIGNoYW5nZXMKLSBEcmF3IGEgdm9sY2FubyBwbG90IHdpdGggdGhlIGBFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgcGVyZm9ybSBhbiBhbmFseXNpcyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIF9NWUNOXyBhbXBsaWZpZWQgdnMuIG5vbmFtcGxpZmllZCBuZXVyb2JsYXN0b21hIGNlbGwgbGluZXMuCgpUaGVzZSBSTkEtc2VxIGRhdGEgYXJlIGZyb20gW0hhcmVuemEsIF9ldCBhbC5fICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykuCgpNb3JlIGluZm9ybWF0aW9uIGFib3V0IERFU2VxMiBjYW4gYmUgZm91bmQgaW4gdGhlIFtleGNlbGxlbnQgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgZnJvbSBMb3ZlLCBBbmRlcnMsIGFuZCBIdWJlciBmcm9tIHdoaWNoIHRoaXMgaXMgYWRhcHRlZCAoc2VlIGFsc286IFtMb3ZlLCBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KSkuCgpERVNlcTIgdGFrZXMgdW5ub3JtYWxpemVkIGNvdW50cyBvciBlc3RpbWF0ZWQgY291bnRzIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoqIFtFc3RpbWF0ZXMgc2l6ZSBmYWN0b3JzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvZXN0aW1hdGVTaXplRmFjdG9ycykKKiBbRXN0aW1hdGVzIGRpc3BlcnNpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ERVNlcTIvdmVyc2lvbnMvMS4xMi4zL3RvcGljcy9lc3RpbWF0ZURpc3BlcnNpb25zKQoqIE5lZ2F0aXZlIGJpbm9taWFsIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCBmaXR0aW5nIGFuZCBbV2FsZCBzdGF0aXN0aWNzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvREVTZXEyL3ZlcnNpb25zLzEuMTIuMy90b3BpY3MvbmJpbm9tV2FsZFRlc3QpCgohW10oZGlhZ3JhbXMvcm5hLXNlcV82LnBuZykKCiMjIExpYnJhcmllcyBhbmQgZnVuY3Rpb25zCgpgYGB7ciBsaWJyYXJ5fQojIExvYWQgdGhlIERFU2VxMiBsaWJyYXJ5CmxpYnJhcnkoREVTZXEyKQoKIyBXZSB3aWxsIGJlIG1ha2luZyBmYW5jeSB2b2xjYW5vIHBsb3RzCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKKipJbnB1dCoqCgpgYGB7ciBpbnB1dC1maWxlc30KIyBkaXJlY3Rvcnkgd2l0aCB0aGUgdHhpbWV0YSBwcm9jZXNzZWQgZGF0YQp0eGlfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJOQi1jZWxsIiwgInR4aSIpCnR4aV9maWxlIDwtIGZpbGUucGF0aCh0eGlfZGlyLCAiTkItY2VsbF90eGltZXRhLnJkcyIpCmBgYAoKCioqT3V0cHV0KioKCldlJ2xsIGNyZWF0ZSBhIHJlc3VsdHMgZGlyZWN0b3J5IHRvIGhvbGQgb3VyIHJlc3VsdHMuCgpgYGB7ciByZXN1bHRzLWRpcn0KIyBDcmVhdGUgYSByZXN1bHRzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIk5CLWNlbGwiKQpmczo6ZGlyX2NyZWF0ZShyZXN1bHRzX2RpcikKYGBgCgpXZSB3aWxsIGFsc28gbmVlZCBhIGRpcmVjdG9yeSB0byBzdG9yZSBvdXIgcGxvdHMuCgpgYGB7ciBwbG90cy1kaXIsIGxpdmUgPSBUUlVFfQojIENyZWF0ZSBhIHBsb3RzIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGFscmVhZHkgZXhpc3QKcGxvdHNfZGlyIDwtIGZpbGUucGF0aCgicGxvdHMiLCAiTkItY2VsbCIpCmZzOjpkaXJfY3JlYXRlKHBsb3RzX2RpcikKYGBgCgoKYGBge3Igb3V0cHV0LWZpbGVzfQojIFJEUyBmb3IgdGhlIG91dHB1dCBvZiBERVNlcSBhbmFseXNpcwpkZXNlcV9maWxlIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgIk5CLWNlbGxfREVTZXFfYW1wbGlmaWVkX3Zfbm9uYW1wbGlmaWVkLnJkcyIpCgojIERFU2VxMiByZXN1bHRzIHRhYmxlCmRlc2VxX2RmX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTkItY2VsbF9ERVNlcV9hbXBsaWZpZWRfdl9ub25hbXBsaWZpZWRfcmVzdWx0cy50c3YiKQoKIyBQTkcgb2YgdGhlIHZvbGNhbm8gcGxvdAp2b2xjYW5vX2ZpbGUgPC0gZmlsZS5wYXRoKHBsb3RzX2RpciwgIk5CLWNlbGxfdm9sY2Fuby5wbmciKQpgYGAKCiMjIERFU2VxMgoKIyMjIENyZWF0aW5nIGEgREVTZXEyIGRhdGFzZXQgZnJvbSB0eGltZXRhIG9iamVjdAoKRmlyc3QsIGxldCdzIHJlYWQgaW4gdGhlIGRhdGEgd2UgcHJvY2Vzc2VkIHdpdGggYHR4aW1ldGFgLgoKIyMjIyBQcmVwYXJhdGlvbgoKYGBge3IgcmVhZF9yZHMsIGxpdmUgPSBUUlVFfQojIFJlYWQgaW4gdGhlIFJEUyBmaWxlIHdlIGNyZWF0ZWQgaW4gdGhlIGxhc3Qgbm90ZWJvb2sKZ2VuZV9zdW1tYXJpemVkIDwtIHJlYWRyOjpyZWFkX3Jkcyh0eGlfZmlsZSkKYGBgCgpXZSdyZSBtb3N0IGludGVyZXN0ZWQgaW4gX01ZQ05fIGFtcGxpZmljYXRpb24sIHdoaWNoIHdlIGhhZCBzdG9yZWQgaW4gdGhlIGBzdGF0dXNgIGNvbHVtbiBvZiB0aGUgc2FtcGxlIG1ldGFkYXRhIG9mIGBnZW5lX3N1bW1hcml6ZWRgLgpXaGlsZSB0aGUgc2FtcGxlIG1ldGFkYXRhIGlzIHN0b3JlZCBpbnRlcm5hbGx5IGluIHRoZSBgY29sRGF0YWAgc2xvdCwgdGhlIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgb2JqZWN0IG1ha2VzIGl0IGVhc3kgZm9yIHVzIHRvIGFjY2VzcyBpdCBhcyBpZiBpdCB3ZXJlIGp1c3QgYSBjb2x1bW4gb2YgYSBkYXRhIGZyYW1lLCB1c2luZyB0aGUgZmFtaWxpYXIgYCRgIHN5bnRheC4KCgpgYGB7ciBTdGF0dXMsIGxpdmUgPSBUUlVFfQpnZW5lX3N1bW1hcml6ZWQkc3RhdHVzCmBgYAoKVGhpcyBpcyBzdG9yZWQgYXMgYSBgY2hhcmFjdGVyYCB0eXBlLCBidXQgdG8gZ2l2ZSBhIGJpdCBtb3JlIGluZm9ybWF0aW9uIHRvIGBERVNlcWAsIHdlIHdpbGwgY29udmVydCB0aGlzIHRvIGEgYGZhY3RvcmAuCgpgYGB7ciBzdGF0dXNfZmFjdG9yLCBsaXZlID0gVFJVRX0KZ2VuZV9zdW1tYXJpemVkJHN0YXR1cyA8LSBhcy5mYWN0b3IoZ2VuZV9zdW1tYXJpemVkJHN0YXR1cykKYGBgCgpXZSdsbCB3YW50IHRvIHVzZSB0aGUgIk5vbmFtcGxpZmllZCIgc2FtcGxlcyBhcyBvdXIgX3JlZmVyZW5jZV8uCkxldCdzIGxvb2sgYXQgdGhlIGBsZXZlbHNgIG9mIGBzdGF0dXNgLgoKYGBge3IgbGV2ZWxzfQpsZXZlbHMoZ2VuZV9zdW1tYXJpemVkJHN0YXR1cykKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlc2UgYXJlIGluIGFscGhhYmV0aWNhbCBvcmRlciwgc28gIkFtcGxpZmllZCIgc2FtcGxlcyB3b3VsZCBiZSB0aGUgcmVmZXJlbmNlLgpXZSBjYW4gdXNlIHRoZSBgcmVsZXZlbCgpYCBmdW5jdGlvbiB0byByZW1lZHkgdGhpcy4KCmBgYHtyIHJlbGV2ZWx9CmdlbmVfc3VtbWFyaXplZCRzdGF0dXMgPC0gcmVsZXZlbChnZW5lX3N1bW1hcml6ZWQkc3RhdHVzLCByZWYgPSAiTm9uYW1wbGlmaWVkIikKYGBgCgpgYGB7ciBjaGVjay1sZXZlbHMsIGxpdmUgPSBUUlVFfQojIENoZWNrIHdoYXQgdGhlIGxldmVscyBhcmUgbm93CmxldmVscyhnZW5lX3N1bW1hcml6ZWQkc3RhdHVzKQpgYGAKCgoKIyMjIyBERVNlcSBEYXRhc2V0IGNyZWF0aW9uCgpgYGB7ciBkZHNldCwgbGl2ZSA9IFRSVUV9CiMgQ3JlYXRlIGEgREVTZXEyIGRhdGFzZXQgZnJvbSBgZ2VuZV9zdW1tYXJpemVkYAojIHJlbWVtYmVyIHRoYXQgYHN0YXR1c2AgaXMgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IGhlcmUKZGRzZXQgPC0gREVTZXFEYXRhU2V0KGdlbmVfc3VtbWFyaXplZCwKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH5zdGF0dXMpCmBgYAoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCiMjIyBGaWx0ZXJpbmcgbG93LWV4cHJlc3NlZCBnZW5lcwoKR2VuZXMgdGhhdCBoYXZlIHZlcnkgbG93IGNvdW50cyBhcmUgbm90IGxpa2VseSB0byB5aWVsZCByZWxpYWJsZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzLCBzbyB3ZSB3aWxsIGRvIHNvbWUgbGlnaHQgW3ByZS1maWx0ZXJpbmddKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3ByZS1maWx0ZXJpbmcpLgpXZSB3aWxsIGtlZXAgb25seSBnZW5lcyB3aXRoIHRvdGFsIGNvdW50cyBvZiBhdCBsZWFzdCAxMCBhY3Jvc3MgYWxsIHNhbXBsZXMuCgpgYGB7ciBmaWx0ZXJfZGRzZXR9CiMgY3JlYXRlIGEgdmVjdG9yIG9mIFRSVUUgYW5kIEZBTFNFIHZhbHVlcyB3aGVyZQojIFRSVUUgY29ycmVzcG9uZHMgdG8gZ2VuZXMgd2l0aCBjb3VudHMgb2YgYXQgbGVhc3QgMTAgCmdlbmVzX3RvX2tlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzZXQpKSA+PSAxMAojIHVzZSB3aGljaCgpIHRvIHByZXZlbnQgYW55IE5BcyBzbmVha2luZyB0aHJvdWdoCmRkc2V0IDwtIGRkc2V0W3doaWNoKGdlbmVzX3RvX2tlZXApLCBdCmBgYAoKCiMjIyBUaGUgYERFU2VxKClgIGZ1bmN0aW9uCgpXZSdsbCBub3cgdXNlIHRoZSB3cmFwcGVyIGZ1bmN0aW9uIGBERVNlcSgpYCB0byBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KQXMgbWVudGlvbmVkIGVhcmxpZXIsIHRoaXMgcGVyZm9ybXMgYSBudW1iZXIgb2Ygc3RlcHMsIGluY2x1ZGluZyBhbiBbb3V0bGllciByZW1vdmFsIHByb2NlZHVyZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2FwcHJvYWNoLXRvLWNvdW50LW91dGxpZXJzKS4KRm9yIHRoaXMgcGFydGljdWxhciBkYXRhc2V0LCB0aGVyZSBpcyBhIHByZXR0eSBsYXJnZSBudW1iZXIgb2Ygb3V0bGllcnMsIHdoaWNoIGNhbiBiZSBhIGJpdCBvZiBhIHJlZCBmbGFnLCBidXQgd2Ugd2lsbCBwcm9jZWVkIGZvciBub3cuCgpgYGB7ciBERVNlcX0KZGVzZXFfb2JqZWN0IDwtIERFU2VxKGRkc2V0KQpgYGAKCkxldCdzIHNhdmUgdGhpcyB0byBvdXIgcmVzdWx0cyBmaWxlLgoKYGBge3Igd3JpdGVfcmRzLCBsaXZlID0gVFJVRX0KIyBTYXZlIHRoZSByZXN1bHRzIGFzIGFuIFJEUwpyZWFkcjo6d3JpdGVfcmRzKGRlc2VxX29iamVjdCwgZmlsZSA9IGRlc2VxX2ZpbGUpCmBgYAoKTm93IHdlIHdpbGwgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdHMgdGFibGUuCgpgYGB7ciBkZXNlcV9yZXN1bHRzfQpkZXNlcV9yZXN1bHRzIDwtIHJlc3VsdHMoZGVzZXFfb2JqZWN0KQpkZXNlcV9yZXN1bHRzCmBgYAoKSG93IG1hbnkgZ2VuZXMgd2VyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgKEZEUiA8IDAuMDUpPwoKYGBge3IgcmVzdWx0c19zdW1tYXJ5fQpzdW1tYXJ5KGRlc2VxX3Jlc3VsdHMsIGFscGhhID0gMC4wNSkKYGBgCgoKIyMjIFNocmlua2luZyBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlcwoKVGhlIGVzdGltYXRlcyBvZiBsb2cyIGZvbGQgY2hhbmdlIGNhbGN1bGF0ZWQgYnkgYERFU2VxKClgIGFyZSBub3QgY29ycmVjdGVkIGZvciBleHByZXNzaW9uIGxldmVsLgpUaGlzIG1lYW5zIHRoYXQgd2hlbiBjb3VudHMgYXJlIHNtYWxsLCB3ZSBhcmUgbGlrZWx5IHRvIGVuZCB1cCB3aXRoIHNvbWUgbGFyZ2UgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgb3ZlcmVzdGltYXRlIHRoZSB0cnVlIGV4dGVudCBvZiB0aGUgY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucy4KCldlIGNhbiBjb3JyZWN0IHRoaXMgYnkgYXBwbHlpbmcgYSAic2hyaW5rYWdlIiBwcm9jZWR1cmUsIHdoaWNoIHdpbGwgYWRqdXN0IGxhcmdlIHZhbHVlcyB3aXRoIHNtYWxsIGNvdW50cyBkb3dud2FyZCwgd2hpbGUgcHJlc2VydmluZyB2YWx1ZXMgd2l0aCBsYXJnZXIgY291bnRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIG1vcmUgYWNjdXJhdGUuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGxmY1NocmluaygpYCBmdW5jdGlvbiwgYnV0IGZpcnN0IHdlIG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQvb3IgcG9zaXRpb24gb2YgdGhlICJjb2VmZmljaWVudCIgdGhhdCB3YXMgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIHRoZSBgcmVzdWx0c05hbWVzKClgIGZ1bmN0aW9uCgpgYGB7ciBkZXNlcV9jb2VmfQojIGdldCB0aGUgZGVzZXEgY29lZmZpY2llbnQgbmFtZXM6CnJlc3VsdHNOYW1lcyhkZXNlcV9vYmplY3QpCmBgYAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGBzdGF0dXNgIGNvZWZmaWNpZW50LCB3aGljaCBpcyBpbiBwb3NpdGlvbiAyLgoKVGhlcmUgYXJlIGEgZmV3IG9wdGlvbnMgZm9yIHRoZSBzaHJpbmthZ2UgZXN0aW1hdGlvbi4KVGhlIGRlZmF1bHQgaXMgYGFwZWdsbWAgKFtaaHUgX2V0IGFsLl8gMjAxOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnR5ODk1KSksIGJ1dCB3ZSBoYXZlIGZvdW5kIHRoYXQgdGhpcyBjYW4gYmUgc2Vuc2l0aXZlIHRvIGV4dHJlbWUgb3V0bGllcnMsIHdoaWNoIGFyZSBkZWZpbml0ZWx5IGEgZmFjdG9yIGluIHRoaXMgZGF0YSBzZXQuClNvIGZvciB0aGlzIGRhdGEgc2V0IHdlIHdpbGwgYmUgdXNpbmcgYGFzaHJgIChbU3RlcGhlbnMgMjAxN10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvc3RhdGlzdGljcy9reHcwNDEpKQoKYGBge3IgbGZjX3Nocmlua30KIyBjYWxjdWxhdGUgc2hydW5rZW4gbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMKZGVzZXFfc2hydW5rZW4gPC0gbGZjU2hyaW5rKGRlc2VxX29iamVjdCwKICBjb2VmID0gMiwgIyB0aGUgY29lZmZpY2llbnQgd2Ugd2FudCB0byByZWVzdGltYXRlCiAgdHlwZSA9ICJhc2hyIiAjIFdlIHdpbGwgdXNlIGBhc2hyYCBmb3IgZXN0aW1hdGlvbgopCmBgYAoKTGV0J3MgY29tcGFyZSB0aGUgbG9nMiBmb2xkIGNoYW5nZSBlc3RpbWF0ZXMgZnJvbSB0aGUgdHdvIHJlc3VsdHMgdGFibGVzIGJ5IGNyZWF0aW5nIGEgcGxvdC4KCkZpcnN0IHdlIHdpbGwgY29tYmluZSB0aGUgcmVzdWx0cyBpbnRvIGEgbmV3IGRhdGEgZnJhbWUuCgpgYGB7ciBjb21wYXJlX3Nocmlua30KY29tcGFyaXNvbl9kZiA8LSBkYXRhLmZyYW1lKAogIGxmY19vcmlnaW5hbCA9IGRlc2VxX3Jlc3VsdHMkbG9nMkZvbGRDaGFuZ2UsCiAgbGZjX3NocnVua2VuID0gZGVzZXFfc2hydW5rZW4kbG9nMkZvbGRDaGFuZ2UsCiAgbG9nbWVhbiA9IGxvZzEwKGRlc2VxX3Jlc3VsdHMkYmFzZU1lYW4pCikKYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlIG9yaWdpbmFsIGFuZCBzaHJ1bmtlbiBsb2cyIGZvbGQgY2hhbmdlIHZhbHVlcyB0byBzZWUgd2hhdCBoYXBwZW5lZCBhZnRlciBzaHJpbmthZ2UuCgpgYGB7ciBwbG90X2NvbXBhcmlzb259CmdncGxvdChjb21wYXJpc29uX2RmLAogIGFlcygKICAgIHggPSBsZmNfb3JpZ2luYWwsCiAgICB5ID0gbGZjX3NocnVua2VuLAogICAgY29sb3IgPSBsb2dtZWFuCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMTAsIDEwKSwgeWxpbSA9IGMoLTEwLCAxMCkpICMgem9vbSBpbiBvbiB0aGUgbWlkZGxlCmBgYAoKV2Ugd2lsbCBub3cgZG8gYSBiaXQgb2YgbWFuaXB1bGF0aW9uIHRvIHN0b3JlIHRoZSByZXN1bHRzIGluIGEgZGF0YSBmcmFtZSBhbmQgYWRkIHRoZSBnZW5lIHN5bWJvbHMuCgpgYGB7ciByZXN1bHRzX2RhdGFmcmFtZX0KIyB0aGlzIGlzIG9mIGNsYXNzIERFU2VxUmVzdWx0cyAtLSB3ZSB3YW50IGEgZGF0YSBmcmFtZQpkZXNlcV9kZiA8LSBkZXNlcV9zaHJ1bmtlbiB8PgogICMgY29udmVydCB0byBhIGRhdGEgZnJhbWUKICBhcy5kYXRhLmZyYW1lKCkgfD4KICAjIHRoZSBnZW5lIGlkcyB3ZXJlIHN0b3JlZCBhcyByb3cgbmFtZXMgLS0gbGV0J3MgdGhlbSBhIGNvbHVtbiBmb3IgZWFzeSBkaXNwbGF5CiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gImdlbmVfaWQiKSB8PgogICMgYWRkIG9uIHRoZSBnZW5lIHN5bWJvbHMgZnJvbSB0aGUgb3JpZ2luYWwgZGVzZXEgb2JqZWN0CiAgZHBseXI6Om11dGF0ZShnZW5lX3N5bWJvbCA9IHJvd0RhdGEoZGVzZXFfb2JqZWN0KSRnZW5lX25hbWUpCmBgYAoKTGV0J3MgcHJpbnQgb3V0IHRoZSByZXN1bHRzIHRhYmxlLCBzb3J0ZWQgYnkgbG9nMiBmb2xkIGNoYW5nZS4KVGhlIGhpZ2hlc3QgdmFsdWVzIHNob3VsZCBiZSBnZW5lcyBtb3JlIGV4cHJlc3NlZCBpbiB0aGUgX01ZQ05fIGFtcGxpZmllZCBjZWxsIGxpbmVzLgoKYGBge3Igc29ydGVkX3RhYmxlLCBsaXZlID0gVFJVRX0KIyBQcmludCB0aGUgdGFibGUgc29ydGVkIGJ5IGxvZzJGb2xkQ2hhbmdlCmRlc2VxX2RmIHw+CiAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MobG9nMkZvbGRDaGFuZ2UpKQpgYGAKCk5vdyBsZXQncyB3cml0ZSB0aGUgZnVsbCByZXN1bHRzIHRhYmxlIHRvIGEgZmlsZS4KCmBgYHtyIHdyaXRlX3Rzdn0KcmVhZHI6OndyaXRlX3RzdihkZXNlcV9kZiwgZmlsZSA9IGRlc2VxX2RmX2ZpbGUpCmBgYAoKCiMjIE1ha2luZyBhIFZvbGNhbm8gUGxvdAoKV2l0aCB0aGVzZSBzaHJ1bmtlbiBlZmZlY3Qgc2l6ZXMsIHdlIHdpbGwgZHJhdyBhIHZvbGNhbm8gcGxvdCwgdXNpbmcgdGhlIFtgRW5oYW5jZWRWb2xjYW5vYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20va2V2aW5ibGlnaGUvRW5oYW5jZWRWb2xjYW5vKSB0byBtYWtlIGl0IGEgYml0IGVhc2llci4KVGhpcyBwYWNrYWdlIGF1dG9tYXRpY2FsbHkgY29sb3IgY29kZXMgdGhlIHBvaW50cyBieSBjdXRvZmZzIGZvciBib3RoIHNpZ25pZmljYW5jZSBhbmQgZm9sZCBjaGFuZ2UgYW5kIGxhYmVscyBtYW55IG9mIHRoZSBzaWduaWZpY2FudCBnZW5lcyAoc3ViamVjdCB0byBzcGFjaW5nKS4KYEVuaGFuY2VkVm9sY2Fub2AgaGFzIG1hbnksIG1hbnkgb3B0aW9ucywgd2hpY2ggaXMgYSBnb29kIHRoaW5nIGlmIHlvdSBkb24ndCBsaWtlIGFsbCBvZiBpdCdzIGRlZmF1bHQgc2V0dGluZ3MuCkV2ZW4gYmV0dGVyLCBpdCBvdXRwdXRzIGEgYGdncGxvdDJgIG9iamVjdCwgc28gaWYgd2Ugd2FudCB0byBjdXN0b21pemUgaXQgZnVydGhlciwgd2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgc2FtZSBgZ2dwbG90MmAgY29tbWFuZHMgd2UgaGF2ZSB1c2VkIGJlZm9yZS4KCmBgYHtyIHZvbGNhbm99CkVuaGFuY2VkVm9sY2FubyhkZXNlcV9kZiwKICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwgIyBmb2xkIGNoYW5nZSBzdGF0aXN0aWMgdG8gcGxvdAogIHkgPSAicHZhbHVlIiwgIyBzaWduaWZpY2FuY2UgdmFsdWVzCiAgbGFiID0gZGVzZXFfZGYkZ2VuZV9zeW1ib2wsICMgbGFiZWxzIGZvciBwb2ludHMKICBwQ3V0b2ZmID0gMWUtMDUsICMgVGhlIHAgdmFsdWUgY3V0b2ZmIHdlIHdpbGwgdXNlIChkZWZhdWx0KQogIEZDY3V0b2ZmID0gMSwgIyBUaGUgZm9sZCBjaGFuZ2UgY3V0b2ZmIChkZWZhdWx0KQogIHRpdGxlID0gTlVMTCwgIyBubyB0aXRsZQogIHN1YnRpdGxlID0gTlVMTCwgIyBvciBzdWJ0aXRsZQogIGNhcHRpb24gPSBOVUxMLCAjIG9yIGNhcHRpb24KICBsYWJTaXplID0gMyAjIHNtYWxsZXIgbGFiZWxzCikgKwogICMgY2hhbmdlIHRoZSBvdmVyYWxsIHRoZW1lCiAgdGhlbWVfY2xhc3NpYygpICsKICAjIG1vdmUgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKV2Ugd2lsbCBzYXZlIHRoaXMgcGxvdCB0byBhIGZpbGUgYXMgd2VsbDoKCmBgYHtyIHNhdmVfcGxvdH0KZ2dzYXZlKHZvbGNhbm9fZmlsZSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCgojIyBTZXNzaW9uIEluZm8KClJlY29yZCBzZXNzaW9uIGluZm8gZm9yIHJlcHJvZHVjaWJpbGl0eSAmIHByb3ZlbmFuY2UgcHVycG9zZXMuCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
diff --git a/RNA-seq/06-openpbta_heatmap-live.Rmd b/RNA-seq/06-openpbta_heatmap-live.Rmd
index 7de8399a..8da2be99 100644
--- a/RNA-seq/06-openpbta_heatmap-live.Rmd
+++ b/RNA-seq/06-openpbta_heatmap-live.Rmd
@@ -20,7 +20,7 @@ This notebook will demonstrate how to:
---
In this notebook, we cluster RNA-seq data from the Open Pediatric Brain Tumor Atlas (OpenPBTA) project and create a heatmap.
-OpenPBTA is a collaborative project organized by the CCDL and the Center for Data-Driven Discovery in Biomedicine (D3b) at the Children's Hospital of Philadelphia conducted openly on GitHub.
+OpenPBTA is a collaborative project organized by the Data Lab and the Center for Data-Driven Discovery in Biomedicine (D3b) at the Children's Hospital of Philadelphia conducted openly on GitHub.
You can read more about the project [here](https://github.com/alexslemonade/openpbta-analysis/#openpbta-analysis).
@@ -64,7 +64,7 @@ We have stored the data we'll use in this notebook in `data/open-pbta`.
histologies_file <- file.path(data_dir, "pbta-histologies-subset.tsv")
# The RNA-seq counts table
-rnaseq_file = file.path(data_dir, "pbta-rsem-expected_count-subset.rds")
+rnaseq_file <- file.path(data_dir, "pbta-rsem-expected_count-subset.rds")
```
#### Output files
@@ -302,7 +302,7 @@ Heatmap(zscores_mat,
name = "z-score")
```
-`ComplexHeatmap` gives a few warning here, but nothing to be concerned about.
+`ComplexHeatmap` gives a few warnings here, but nothing to be concerned about.
One is complaining that the color scale may be truncated relative to our data.
The other warns that our very large heatmap itself is being drawn at a lower resolution to speed things up.
diff --git a/RNA-seq/06-openpbta_heatmap.nb.html b/RNA-seq/06-openpbta_heatmap.nb.html
index c27172c4..d6e6cfa8 100644
--- a/RNA-seq/06-openpbta_heatmap.nb.html
+++ b/RNA-seq/06-openpbta_heatmap.nb.html
@@ -2920,26 +2920,135 @@ Set up
Libraries
-
-# We will manipulate RNASeq data with DESeq2 at the start
+
+# We will manipulate RNASeq data with DESeq2 at the start
+library(DESeq2)
-
-Warning message:
-replacing previous import ‘S4Arrays::makeNindexFromArrayViewport’ by ‘DelayedArray::makeNindexFromArrayViewport’ when loading ‘SummarizedExperiment’
-
-
-library(DESeq2)
-
-# Then we'll be doing a bit of data wrangling with the Tidyverse
+
+Loading required package: S4Vectors
+
+
+Loading required package: stats4
+
+
+Loading required package: BiocGenerics
+
+
+
+Attaching package: 'BiocGenerics'
+
+
+The following objects are masked from 'package:stats':
+
+ IQR, mad, sd, var, xtabs
+
+
+The following objects are masked from 'package:base':
+
+ anyDuplicated, aperm, append, as.data.frame, basename, cbind,
+ colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
+ get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
+ match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
+ Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
+ tapply, union, unique, unsplit, which.max, which.min
+
+
+
+Attaching package: 'S4Vectors'
+
+
+The following object is masked from 'package:utils':
+
+ findMatches
+
+
+The following objects are masked from 'package:base':
+
+ expand.grid, I, unname
+
+
+Loading required package: IRanges
+
+
+Loading required package: GenomicRanges
+
+
+Loading required package: GenomeInfoDb
+
+
+Loading required package: SummarizedExperiment
+
+
+Loading required package: MatrixGenerics
+
+
+Loading required package: matrixStats
+
+
+
+Attaching package: 'MatrixGenerics'
+
+
+The following objects are masked from 'package:matrixStats':
+
+ colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
+ colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
+ colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
+ colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
+ colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
+ colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
+ colWeightedMeans, colWeightedMedians, colWeightedSds,
+ colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
+ rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
+ rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
+ rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
+ rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
+ rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
+ rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
+ rowWeightedSds, rowWeightedVars
+
+
+Loading required package: Biobase
+
+
+Welcome to Bioconductor
+
+ Vignettes contain introductory material; view with
+ 'browseVignettes()'. To cite Bioconductor, see
+ 'citation("Biobase")', and for packages 'citation("pkgname")'.
+
+
+
+Attaching package: 'Biobase'
+
+
+The following object is masked from 'package:MatrixGenerics':
+
+ rowMedians
+
+
+The following objects are masked from 'package:matrixStats':
+
+ anyMissing, rowMedians
+
+
+Warning: replacing previous import 'S4Arrays::makeNindexFromArrayViewport' by
+'DelayedArray::makeNindexFromArrayViewport' when loading 'SummarizedExperiment'
+
+
+# Then we'll be doing a bit of data wrangling with the Tidyverse
library(tidyverse)
-
-── Attaching core tidyverse packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
+
+── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
-✔ purrr 1.0.2 ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
+✔ purrr 1.0.2
+
+
+── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ lubridate::%within%() masks IRanges::%within%()
✖ dplyr::collapse() masks IRanges::collapse()
✖ dplyr::combine() masks Biobase::combine(), BiocGenerics::combine()
@@ -2955,14 +3064,14 @@ Libraries
✖ lubridate::second() masks S4Vectors::second()
✖ lubridate::second<-() masks S4Vectors::second<-()
✖ dplyr::slice() masks IRanges::slice()
-ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
-
-
+ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
+
+
# ComplexHeatmap is the package we'll use for making a heatmap
# It will do the hierarchical clustering for us as well
library(ComplexHeatmap)
-
+
Loading required package: grid
========================================
ComplexHeatmap version 2.20.0
@@ -2982,7 +3091,7 @@ Libraries
This message can be suppressed by:
suppressPackageStartupMessages(library(ComplexHeatmap))
========================================
-
+
@@ -2992,7 +3101,7 @@ Directories and files
data/open-pbta
.
-
+
data_dir <- file.path("data", "open-pbta")
# We'll store the heatmap in plots/open-pbta - create directory if it doesn't exist yet
@@ -3005,7 +3114,7 @@ Directories and files
Input files
-
+
# The metadata describing the samples
histologies_file <- file.path(data_dir, "pbta-histologies-subset.tsv")
@@ -3019,7 +3128,7 @@ Input files
Output files
-
+
heatmap_file <- file.path(plots_dir,
"common_histologies_high_variance_heatmap.png")
@@ -3035,17 +3144,19 @@ Metadata
Let’s read in the metadata file file and take a look at the data.
-
+
histologies_df <- read_tsv(histologies_file)
-
-Rows: 607 Columns: 33── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+
+Rows: 607 Columns: 33
+── Column specification ────────────────────────────────────────────────────────
Delimiter: "\t"
-chr (31): Kids_First_Biospecimen_ID, CNS_region, sample_id, aliquot_id, Kids_First_Participant_ID, experimental_strategy, sample_type, composition, tumor_descriptor, primary_site, reported_gender, race...
+chr (31): Kids_First_Biospecimen_ID, CNS_region, sample_id, aliquot_id, Kids...
dbl (2): OS_days, age_last_update_days
+
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
-
+
Use the chunk below to explore the metadata data frame.
@@ -3062,7 +3173,7 @@ Metadata
the Tidyverse.
-
+
histology_count_df <- histologies_df |>
# Count how many samples are in each short_histology and name the column
# with that number n
@@ -3073,13 +3184,11 @@ Metadata
histology_count_df
-
-
@@ -3088,7 +3197,7 @@ RNA-seq data
Read in the expression count matrix (stored as a data frame).
-
+
# Read in and examine the RNA-seq data
rnaseq_exp <- read_rds(rnaseq_file)
@@ -3104,7 +3213,7 @@ Convert and round
convert from a data frame to a matrix.
-
+
rnaseq_mat <- rnaseq_exp |>
# move gene_id to the rownames
tibble::column_to_rownames("gene_id") |>
@@ -3128,7 +3237,7 @@ Variance Stabilizing Transformation
matrix.
-
+
all.equal(histologies_df$Kids_First_Biospecimen_ID,
colnames(rnaseq_mat))
@@ -3143,21 +3252,21 @@ Variance Stabilizing Transformation
experimental design at this stage.
-
+
ddset <- DESeqDataSetFromMatrix(rnaseq_mat,
colData = histologies_df,
design = ~ 1) # don't store an experimental design
-
+
converting counts to integer mode
-
+
We will again remove low count genes, as they are not likely to be
informative.
-
+
genes_to_keep <- rowSums(counts(ddset)) >= 10
ddset <- ddset[genes_to_keep, ]
@@ -3167,7 +3276,7 @@ Variance Stabilizing Transformation
results in a new object.
-
+
# apply variance stabilizing transformation
vst_data <- vst(ddset, blind = TRUE)
@@ -3178,7 +3287,7 @@ Variance Stabilizing Transformation
which we can extract with assay()
.
-
+
# extract transformed data
expr_mat <- assay(vst_data)
@@ -3187,7 +3296,7 @@ Variance Stabilizing Transformation
What are the dimensions of this transformed RNA-seq data matrix?
-
+
dim(expr_mat)
@@ -3205,7 +3314,7 @@ Variance Stabilizing Transformation
then take the genes in the top 10%.
-
+
# Calculate variance from the expression data
gene_variance <- matrixStats::rowVars(expr_mat)
@@ -3218,15 +3327,19 @@ Variance Stabilizing Transformation
# What does a row index look like?
head(high_variance_index)
-
- ENSG00000000971.15_CFH ENSG00000001617.11_SEMA3F ENSG00000002586.18_CD99 ENSG00000002586.18_PAR_Y_CD99 ENSG00000002587.9_HS3ST1 ENSG00000002745.12_WNT16
- 7 15 24 25 26 28
+
+ ENSG00000000971.15_CFH ENSG00000001617.11_SEMA3F
+ 7 15
+ ENSG00000002586.18_CD99 ENSG00000002586.18_PAR_Y_CD99
+ 24 25
+ ENSG00000002587.9_HS3ST1 ENSG00000002745.12_WNT16
+ 26 28
-
+
# Get a matrix that is subset to just the high variance genes
high_var_mat <- expr_mat[high_variance_index, ]
@@ -3243,7 +3356,7 @@ Annotation
called annotation, or HeatmapAnnotation
, specifically.
-
+
sample_annotation_df <- histologies_df |>
# Select only the columns that we'll use
select(Kids_First_Biospecimen_ID,
@@ -3254,13 +3367,11 @@ Annotation
# Let's examine these columns
sample_annotation_df
-
-
ComplexHeatmap
is going to want the data frame we
@@ -3268,7 +3379,7 @@
Annotation
up.
-
+
sample_annotation_df <- sample_annotation_df |>
tibble::column_to_rownames("Kids_First_Biospecimen_ID")
@@ -3281,7 +3392,7 @@ Annotation
columns.
-
+
# The Okabe Ito palette is recommended for those with color vision deficiencies
histology_colors <- palette.colors(palette = "Okabe-Ito")[2:5]
# `palette.colors()` returns a named vector, which can cause trouble
@@ -3316,7 +3427,7 @@ Annotation
nicer to look at than the raw columns names.
-
+
column_annotation <- HeatmapAnnotation(
df = sample_annotation_df,
col = sample_annotation_colors,
@@ -3336,7 +3447,7 @@ Values for display
mean of 0 and a standard deviation of 1.
-
+
zscores_mat <-
(high_var_mat - rowMeans(high_var_mat)) / matrixStats::rowSds(high_var_mat)
@@ -3352,7 +3463,7 @@ Heatmap itself!
bars.
-
+
Heatmap(zscores_mat,
# The distance metric used for clustering the rows
# This is different from the default (Euclidean)
@@ -3372,17 +3483,23 @@ Heatmap itself!
# of the cells of the heatmap itself
name = "z-score")
-
-The automatically generated colors map from the minus and plus 99^th of the absolute values in the matrix. There are outliers in the matrix whose patterns might be hidden by this
-color mapping. You can manually set the color to `col` argument.
+
+The automatically generated colors map from the minus and plus 99^th of
+the absolute values in the matrix. There are outliers in the matrix
+whose patterns might be hidden by this color mapping. You can manually
+set the color to `col` argument.
-Use `suppressMessages()` to turn off this message.
-`use_raster` is automatically set to TRUE for a matrix with more than 2000 rows. You can control `use_raster` argument by explicitly setting TRUE/FALSE to it.
+Use `suppressMessages()` to turn off this message.
+
+
+`use_raster` is automatically set to TRUE for a matrix with more than
+2000 rows. You can control `use_raster` argument by explicitly setting
+TRUE/FALSE to it.
Set `ht_opt$message = FALSE` to turn off this message.
-
-
-
+
+
+
@@ -3397,7 +3514,7 @@ Heatmap itself!
output.
-
+
# Open PNG plot device
png(filename = heatmap_file,
width = 11,
@@ -3416,19 +3533,21 @@ Heatmap itself!
name = "z-score",
use_raster = FALSE) # higher resolution for output (be careful with PDF output!)
-
-The automatically generated colors map from the minus and plus 99^th of the absolute values in the matrix. There are outliers in the matrix whose patterns might be hidden by this
-color mapping. You can manually set the color to `col` argument.
+
+The automatically generated colors map from the minus and plus 99^th of
+the absolute values in the matrix. There are outliers in the matrix
+whose patterns might be hidden by this color mapping. You can manually
+set the color to `col` argument.
Use `suppressMessages()` to turn off this message.
-
-
+
+
# Shut down current graphics device
dev.off()
-
-null device
- 1
+
+png
+ 2
@@ -3473,17 +3592,17 @@ PCA as an alternative to clustering
plotPCA()
function from DESeq2
.
-
+
# Use plotPCA, but return the data for custom plotting
pca_df <- plotPCA(vst_data,
ntop = 5000, # use the top 5000 genes by variance
intgroup = "short_histology",
returnData = TRUE)
-
+
using ntop=5000 top features by variance
-
-
+
+
ggplot(pca_df, aes(PC1, PC2, color = short_histology)) +
geom_point() +
theme_bw() +
@@ -3495,8 +3614,8 @@ PCA as an alternative to clustering
) +
labs(color = "Histology")
-
-
+
+
@@ -3515,11 +3634,11 @@ PCA as an alternative to clustering
Session Info
-
+
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3528,30 +3647,60 @@ Session Info
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
locale:
- [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8 LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8 LC_PAPER=C.UTF-8 LC_NAME=C
- [9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
+ [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
+ [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
+ [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
+ [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
+ [9] LC_ADDRESS=C LC_TELEPHONE=C
+[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Etc/UTC
tzcode source: system (glibc)
attached base packages:
-[1] grid stats4 stats graphics grDevices utils datasets methods base
+[1] grid stats4 stats graphics grDevices utils datasets
+[8] methods base
other attached packages:
- [1] ComplexHeatmap_2.20.0 lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4 purrr_1.0.2 readr_2.1.5
- [8] tidyr_1.3.1 tibble_3.2.1 ggplot2_3.5.1 tidyverse_2.0.0 DESeq2_1.44.0 SummarizedExperiment_1.34.0 Biobase_2.64.0
-[15] MatrixGenerics_1.16.0 matrixStats_1.3.0 GenomicRanges_1.56.0 GenomeInfoDb_1.40.0 IRanges_2.38.0 S4Vectors_0.42.0 BiocGenerics_0.50.0
+ [1] ComplexHeatmap_2.20.0 lubridate_1.9.3
+ [3] forcats_1.0.0 stringr_1.5.1
+ [5] dplyr_1.1.4 purrr_1.0.2
+ [7] readr_2.1.5 tidyr_1.3.1
+ [9] tibble_3.2.1 ggplot2_3.5.1
+[11] tidyverse_2.0.0 DESeq2_1.44.0
+[13] SummarizedExperiment_1.34.0 Biobase_2.64.0
+[15] MatrixGenerics_1.16.0 matrixStats_1.3.0
+[17] GenomicRanges_1.56.0 GenomeInfoDb_1.40.0
+[19] IRanges_2.38.0 S4Vectors_0.42.0
+[21] BiocGenerics_0.50.0 optparse_1.7.5
loaded via a namespace (and not attached):
- [1] tidyselect_1.2.1 farver_2.1.1 digest_0.6.35 timechange_0.3.0 lifecycle_1.0.4 cluster_2.1.6 Cairo_1.6-2 magrittr_2.0.3
- [9] compiler_4.4.0 rlang_1.1.3 tools_4.4.0 utf8_1.2.4 knitr_1.46 labeling_0.4.3 S4Arrays_1.4.0 bit_4.0.5
-[17] DelayedArray_0.30.0 RColorBrewer_1.1-3 abind_1.4-5 BiocParallel_1.38.0 withr_3.0.0 fansi_1.0.6 colorspace_2.1-0 scales_1.3.0
-[25] iterators_1.0.14 cli_3.6.2 crayon_1.5.2 generics_0.1.3 rstudioapi_0.16.0 httr_1.4.7 tzdb_0.4.0 rjson_0.2.21
-[33] zlibbioc_1.50.0 parallel_4.4.0 XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0 jsonlite_1.8.8 GetoptLong_1.0.5 hms_1.1.3
-[41] bit64_4.0.5 clue_0.3-65 magick_2.8.3 locfit_1.5-9.9 foreach_1.5.2 glue_1.7.0 codetools_0.2-20 stringi_1.8.3
-[49] shape_1.4.6.1 gtable_0.3.5 UCSC.utils_1.0.0 munsell_0.5.1 pillar_1.9.0 GenomeInfoDbData_1.2.12 circlize_0.4.16 R6_2.5.1
-[57] doParallel_1.0.17 vroom_1.6.5 lattice_0.22-6 png_0.1-8 Rcpp_1.0.12 SparseArray_1.4.0 xfun_0.43 fs_1.6.4
-[65] pkgconfig_2.0.3 GlobalOptions_0.1.2
+ [1] tidyselect_1.2.1 farver_2.1.1 fastmap_1.1.1
+ [4] digest_0.6.35 timechange_0.3.0 lifecycle_1.0.4
+ [7] cluster_2.1.6 Cairo_1.6-2 magrittr_2.0.3
+[10] compiler_4.4.1 rlang_1.1.3 sass_0.4.9
+[13] tools_4.4.1 utf8_1.2.4 yaml_2.3.8
+[16] knitr_1.46 labeling_0.4.3 S4Arrays_1.4.0
+[19] bit_4.0.5 DelayedArray_0.30.0 RColorBrewer_1.1-3
+[22] abind_1.4-5 BiocParallel_1.38.0 withr_3.0.0
+[25] fansi_1.0.6 colorspace_2.1-0 scales_1.3.0
+[28] iterators_1.0.14 cli_3.6.2 rmarkdown_2.26
+[31] crayon_1.5.2 generics_0.1.3 httr_1.4.7
+[34] tzdb_0.4.0 rjson_0.2.21 getopt_1.20.4
+[37] cachem_1.0.8 zlibbioc_1.50.0 parallel_4.4.1
+[40] XVector_0.44.0 vctrs_0.6.5 Matrix_1.7-0
+[43] jsonlite_1.8.8 hms_1.1.3 GetoptLong_1.0.5
+[46] bit64_4.0.5 clue_0.3-65 magick_2.8.3
+[49] locfit_1.5-9.9 foreach_1.5.2 jquerylib_0.1.4
+[52] glue_1.7.0 codetools_0.2-20 shape_1.4.6.1
+[55] stringi_1.8.3 gtable_0.3.5 UCSC.utils_1.0.0
+[58] munsell_0.5.1 pillar_1.9.0 htmltools_0.5.8.1
+[61] GenomeInfoDbData_1.2.12 circlize_0.4.16 R6_2.5.1
+[64] doParallel_1.0.17 vroom_1.6.5 evaluate_0.23
+[67] lattice_0.22-6 highr_0.10 png_0.1-8
+[70] bslib_0.7.0 Rcpp_1.0.12 SparseArray_1.4.0
+[73] xfun_0.43 fs_1.6.4 pkgconfig_2.0.3
+[76] GlobalOptions_0.1.2
diff --git a/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd b/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd
index 4d94a5fd..be1e6fa7 100644
--- a/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd
+++ b/intro-to-R-tidyverse/01-intro_to_base_R-live.Rmd
@@ -24,7 +24,7 @@ This notebook will demonstrate how to:
#### *More resources for learning R*
- [Swirl, an interactive tutorial](https://swirlstats.com/)
-- [_R for Data Science_ book](https://r4ds.had.co.nz/)
+- [_R for Data Science_ book](https://r4ds.hadley.nz/)
- [Tutorial on R, RStudio and R Markdown](https://ismayc.github.io/rbasics-book/)
- [Handy R cheatsheets](https://www.posit.co/resources/cheatsheets/)
- [R Markdown website](https://rmarkdown.rstudio.com)
diff --git a/intro-to-R-tidyverse/01-intro_to_base_R.nb.html b/intro-to-R-tidyverse/01-intro_to_base_R.nb.html
index efd95791..ce772647 100644
--- a/intro-to-R-tidyverse/01-intro_to_base_R.nb.html
+++ b/intro-to-R-tidyverse/01-intro_to_base_R.nb.html
@@ -2910,7 +2910,7 @@ More resources for learning R
Swirl, an interactive
tutorial
-R for Data Science
+R for Data Science
book
Tutorial on R,
@@ -3888,8 +3888,8 @@ Session Info
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3923,17 +3923,17 @@ Session Info
[16] evaluate_0.23 jquerylib_0.1.4 tibble_3.2.1
[19] tzdb_0.4.0 fastmap_1.1.1 yaml_2.3.8
[22] lifecycle_1.0.4 palmerpenguins_0.1.1 stringr_1.5.1
-[25] compiler_4.4.0 getopt_1.20.4 pkgconfig_2.0.3
+[25] compiler_4.4.1 getopt_1.20.4 pkgconfig_2.0.3
[28] digest_0.6.35 R6_2.5.1 tidyselect_1.2.1
-[31] utf8_1.2.4 parallel_4.4.0 vroom_1.6.5
+[31] utf8_1.2.4 parallel_4.4.1 vroom_1.6.5
[34] pillar_1.9.0 magrittr_2.0.3 bslib_0.7.0
-[37] bit64_4.0.5 tools_4.4.0 cachem_1.0.8
+[37] bit64_4.0.5 tools_4.4.1 cachem_1.0.8
-LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgYW5kIFJTdHVkaW8iCmF1dGhvcjogT3JpZ2luYWxseSBhdXRob3JlZCBieSBTdGVwaGFuaWUgSi4gU3BpZWxtYW4sPGJyPmFkYXB0ZWQgYnkgQ0NETCBmb3IgQUxTRgpkYXRlOiAyMDIxCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOiAgCgotIE5hdmlnYXRlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50ICAKLSBVc2UgUiBmb3Igc2ltcGxlIGNhbGN1bGF0aW9ucywgYm90aCBtYXRoZW1hdGljYWwgYW5kIGxvZ2ljYWwgIAotIERlZmluZSBhbmQgdXNlIHZhcmlhYmxlcyBpbiBiYXNlIFIgIAotIFVuZGVyc3RhbmQgYW5kIGFwcGx5IGJhc2UgUiBmdW5jdGlvbnMgICAKLSBVbmRlcnN0YW5kLCBkZWZpbmUsIGFuZCB1c2UgUiBkYXRhIHR5cGVzLCBpbmNsdWRpbmcgdmVjdG9yIG1hbmlwdWxhdGlvbiBhbmQgaW5kZXhpbmcgIAotIFVuZGVyc3RhbmQgdGhlIGFuYXRvbXkgb2YgYSBkYXRhIGZyYW1lICAKCi0tLQoKIyMjIyAqTW9yZSByZXNvdXJjZXMgZm9yIGxlYXJuaW5nIFIqIAoKLSBbU3dpcmwsIGFuIGludGVyYWN0aXZlIHR1dG9yaWFsXShodHRwczovL3N3aXJsc3RhdHMuY29tLykgIAotIFtfUiBmb3IgRGF0YSBTY2llbmNlXyBib29rXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgIAotIFtUdXRvcmlhbCBvbiBSLCBSU3R1ZGlvIGFuZCBSIE1hcmtkb3duXShodHRwczovL2lzbWF5Yy5naXRodWIuaW8vcmJhc2ljcy1ib29rLykgIAotIFtIYW5keSBSIGNoZWF0c2hlZXRzXShodHRwczovL3d3dy5wb3NpdC5jby9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAgCi0gW1IgTWFya2Rvd24gd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pICAKLSBbX1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlX10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgIAoKIyMgV2hhdCBpcyBSPwoKKipSKiogaXMgYSBzdGF0aXN0aWNhbCBjb21wdXRpbmcgbGFuZ3VhZ2UgdGhhdCBpcyBfb3BlbiBzb3VyY2VfLCBtZWFuaW5nIHRoZSB1bmRlcmx5aW5nIGNvZGUgZm9yIHRoZSBsYW5ndWFnZSBpcyBmcmVlbHkgYXZhaWxhYmxlIHRvIGFueW9uZS4gCllvdSBkbyBub3QgbmVlZCBhIHNwZWNpYWwgbGljZW5zZSBvciBzZXQgb2YgcGVybWlzc2lvbnMgdG8gdXNlIGFuZCBkZXZlbG9wIGNvZGUgaW4gUi4gCgpSIGl0c2VsZiBpcyBhbiBfaW50ZXJwcmV0ZWQgY29tcHV0ZXIgbGFuZ3VhZ2VfIGFuZCBjb21lcyB3aXRoIGZ1bmN0aW9uYWxpdHkgdGhhdCBjb21lcyBidW5kbGVkIHdpdGggdGhlIGxhbmd1YWdlIGl0c2VsZiwga25vd24gYXMgKioiYmFzZSBSIioqLgpCdXQgdGhlcmUgaXMgYWxzbyByaWNoIGFkZGl0aW9uYWwgZnVuY3Rpb25hbGl0eSBwcm92aWRlZCBieSAqKmV4dGVybmFsIHBhY2thZ2VzKiosIG9yIGxpYnJhcmllcyBvZiBjb2RlIHRoYXQgYXNzaXN0IGluIGFjY29tcGxpc2hpbmcgY2VydGFpbiB0YXNrcyBhbmQgY2FuIGJlIGZyZWVseSBkb3dubG9hZGVkIGFuZCBsb2FkZWQgZm9yIHVzZS4gCgpJbiB0aGUgbmV4dCBub3RlYm9vayBhbmQgc3Vic2VxdWVudCBtb2R1bGVzLCB3ZSB3aWxsIGJlIHVzaW5nIGEgc3VpdGUgb2YgcGFja2FnZXMgY29sbGVjdGl2ZWx5IGtub3duIGFzIFsqKlRoZSBUaWR5dmVyc2UqKl0oaHR0cHM6Ly90aWR5dmVyc2Uub3JnKS4gClRoZSBgdGlkeXZlcnNlYCBpcyBnZWFyZWQgdG93YXJkcyBpbnR1aXRpdmUgZGF0YSBzY2llbmNlIGFwcGxpY2F0aW9ucyB0aGF0IGZvbGxvdyBhIHNoYXJlZCBkYXRhIHBoaWxvc29waHkuCkJ1dCB0aGVyZSBhcmUgc3RpbGwgbWFueSBjb3JlIGZlYXR1cmVzIG9mIGJhc2UgUiB3aGljaCBhcmUgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mLCBhbmQgd2Ugd2lsbCBiZSB1c2luZyBjb25jZXB0cyBmcm9tIGJvdGggYmFzZSBSIGFuZCB0aGUgdGlkeXZlcnNlIGluIG91ciBhbmFseXNlcywgYXMgd2VsbCBhcyB0YXNrIHNwZWNpZmljIHBhY2thZ2VzIGZvciBhbmFseXNlcyBzdWNoIGFzIGdlbmUgZXhwcmVzc2lvbi4gCgojIyMgV2hhdCBpcyBSU3R1ZGlvPwoKUlN0dWRpbyBpcyBhIF9ncmFwaGljYWwgZW52aXJvbm1lbnRfICgiaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCIgb3IgSURFKSBmb3Igd3JpdGluZyBhbmQgZGV2ZWxvcGluZyBSIGNvZGUuIFJTdHVkaW8gaXMgTk9UIGEgc2VwYXJhdGUgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgLSBpdCBpcyBhbiBpbnRlcmZhY2Ugd2UgdXNlIHRvIGZhY2lsaXRhdGUgUiBwcm9ncmFtbWluZy4gCkluIG90aGVyIHdvcmRzLCB5b3UgY2FuIHByb2dyYW0gaW4gUiB3aXRob3V0IFJTdHVkaW8sIGJ1dCB5b3UgY2FuJ3QgdXNlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50IHdpdGhvdXQgUi4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IFJTdHVkaW8gdGhhbiB5b3UgZXZlciB3YW50ZWQgdG8ga25vdywgc2VlIHRoaXMgW1JTdHVkaW8gSURFIENoZWF0c2hlZXQgKHBkZildKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYWluL3JzdHVkaW8taWRlLnBkZikuCgojIyBUaGUgUlN0dWRpbyBFbnZpcm9ubWVudAoKVGhlIFJTdHVkaW8gZW52aXJvbm1lbnQgaGFzIGZvdXIgbWFpbiAqKnBhbmVzKiosIGVhY2ggb2Ygd2hpY2ggbWF5IGhhdmUgYSBudW1iZXIgb2YgdGFicyB0aGF0IGRpc3BsYXkgZGlmZmVyZW50IGluZm9ybWF0aW9uIG9yIGZ1bmN0aW9uYWxpdHkuICh0aGVpciBzcGVjaWZpYyBsb2NhdGlvbiBjYW4gYmUgY2hhbmdlZCB1bmRlciBUb29scyAtPiBHbG9iYWwgT3B0aW9ucyAtPiBQYW5lIExheW91dCkuCiFbUlN0dWRpbyBBcHBlYXJhbmNlXShzY3JlZW5zaG90cy9yc3R1ZGlvLXBhbmVzLnBuZykgCgoxLiBUaGUgKipFZGl0b3IqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gd3JpdGUgUiBzY3JpcHRzIGFuZCBvdGhlciBkb2N1bWVudHMuIEVhY2ggdGFiIGhlcmUgaXMgaXRzIG93biBkb2N1bWVudC4KVGhpcyBpcyB5b3VyIF90ZXh0IGVkaXRvcl8sIHdoaWNoIHdpbGwgYWxsb3cgeW91IHRvIHNhdmUgeW91ciBSIGNvZGUgZm9yIGZ1dHVyZSB1c2UuIApOb3RlIHRoYXQgY2hhbmdlIGNvZGUgaGVyZSB3aWxsIG5vdCBydW4gYXV0b21hdGljYWxseSB1bnRpbCB5b3UgcnVuIGl0LiAKCjIuIFRoZSAqKkNvbnNvbGUqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gX2ludGVyYWN0aXZlbHlfIHJ1biBSIGNvZGUuIAogICsgVGhlcmUgaXMgYWxzbyBhICoqVGVybWluYWwqKiB0YWIgaGVyZSB3aGljaCBjYW4gYmUgdXNlZCBmb3IgcnVubmluZyBwcm9ncmFtcyBvdXRzaWRlIFIgb24geW91ciBjb21wdXRlcgogIAozLiBUaGUgKipFbnZpcm9ubWVudCoqIHBhbmUgcHJpbWFyaWx5IGRpc3BsYXlzIHRoZSB2YXJpYWJsZXMsIHNvbWV0aW1lcyBrbm93biBhcyBfb2JqZWN0c18gdGhhdCBhcmUgZGVmaW5lZCBkdXJpbmcgYSBnaXZlbiBSIHNlc3Npb24sIGFuZCB3aGF0IGRhdGEgb3IgdmFsdWVzIHRoZXkgbWlnaHQgaG9sZC4KCjQuIFRoZSBmaW5hbCBwYW5lLCAqKkZpbGVzLCBQbG90cywgSGVscCwgLi4uKiosIGhhcyBzZXZlcmFsIHByZXR0eSBpbXBvcnRhbnQgdGFiczoKICAgICsgVGhlICoqRmlsZXMqKiB0YWIgc2hvd3MgdGhlIHN0cnVjdHVyZSBhbmQgY29udGVudHMgb2YgZmlsZXMgYW5kIGZvbGRlcnMgKGFsc28ga25vd24gYXMgZGlyZWN0b3JpZXMpIG9uIHlvdXIgY29tcHV0ZXIuCiAgICArIFRoZSAqKlBsb3RzKiogdGFiIHdpbGwgcmV2ZWFsIHBsb3RzIHdoZW4geW91IG1ha2UgdGhlbQogICAgKyBUaGUgKipQYWNrYWdlcyoqIHRhYiBzaG93cyB3aGljaCBpbnN0YWxsZWQgcGFja2FnZXMgaGF2ZSBiZWVuIGxvYWRlZCBpbnRvIHlvdXIgUiBzZXNzaW9uCiAgICArIFRoZSAqKkhlbHAqKiB0YWIgd2lsbCBzaG93IHRoZSBoZWxwIHBhZ2Ugd2hlbiB5b3UgbG9vayB1cCBhIGZ1bmN0aW9uCiAgICArIFRoZSAqKlZpZXdlcioqIHRhYiB3aWxsIHJldmVhbCBjb21waWxlZCBSIE1hcmtkb3duIGRvY3VtZW50cwoKIyMgQmFzaWMgQ2FsY3VsYXRpb25zCgojIyMgTWF0aGVtYXRpY2FsIG9wZXJhdG9ycwoKVGhlIG1vc3QgYmFzaWMgdXNlIG9mIFIgaXMgYXMgYSByZWd1bGFyIGNhbGN1bGF0b3I6Cgp8IE9wZXJhdGlvbiB8IFN5bWJvbCB8CnwtLS0tLS0tLS0tLXwtLS0tLS0tLXwKfCBBZGQgIHwgYCtgIHwgCnwgU3VidHJhY3QgIHwgYC1gIHwgCnwgTXVsdGlwbHkgIHwgYCpgIHwgCnwgRGl2aWRlICB8IGAvYCB8IAp8IEV4cG9uZW50aWF0ZSB8IGBeYCBvciBgKipgIHwgCgpGb3IgZXhhbXBsZSwgd2UgY2FuIGRvIHNvbWUgc2ltcGxlIG11bHRpcGxpY2F0aW9uIGxpa2UgdGhpcy4gCldoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgCnBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKiBvbiBhIE1hYywgb3IgKkN0cmwrU2hpZnQrRW50ZXIqIG9uIGEgUEMuCgpgYGB7ciBjYWxjdWxhdG9yfQo1ICogNgpgYGAKClVzZSB0aGUgY29uc29sZSB0byBjYWxjdWxhdGUgb3RoZXIgZXhwcmVzc2lvbnMuIFN0YW5kYXJkIG9yZGVyIG9mIG9wZXJhdGlvbnMgYXBwbGllcyAobW9zdGx5KSwgYW5kICB5b3UgY2FuIHVzZSBwYXJlbnRoZXNlcyBgKClgIGFzIHlvdSBtaWdodCBleHBlY3QgKGJ1dCBub3QgYnJhY2tldHMgYFtdYCBvciBicmFjZXNge31gLCB3aGljaCBoYXZlIHNwZWNpYWwgbWVhbmluZ3MpLiBOb3RlIGhvd2V2ZXIsIHRoYXQgeW91IG11c3QgKiphbHdheXMqKiBzcGVjaWZ5IG11bHRpcGxpY2F0aW9uIHdpdGggYCpgOyBpbXBsaWNpdCBtdWx0aXBsaWNhdGlvbiBzdWNoIGFzIGAxMCgzICsgNClgIG9yIGAxMHhgIHdpbGwgbm90IHdvcmsgYW5kIHdpbGwgZ2VuZXJhdGUgYW4gZXJyb3IsIG9yIHdvcnNlLgoKYGBge3IgZXhwcmVzc2lvbnMsIGxpdmUgPSBUUlVFfQoxMCAqICgzICsgNCleMgpgYGAKCgojIyMgRGVmaW5pbmcgYW5kIHVzaW5nIHZhcmlhYmxlcyAKClRvIGRlZmluZSBhIHZhcmlhYmxlLCB3ZSB1c2UgdGhlIF9hc3NpZ25tZW50IG9wZXJhdG9yXyB3aGljaCBsb29rcyBsaWtlIGFuIGFycm93OiBgPC1gLCBmb3IgZXhhbXBsZSBgeCA8LSA3YCB0YWtlcyB0aGUgdmFsdWUgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB0aGUgb3BlcmF0b3IgYW5kIGFzc2lnbnMgaXQgdG8gdGhlIHZhcmlhYmxlIG5hbWUgb24gdGhlIGxlZnQtaGFuZCBzaWRlLiAKCmBgYHtyIHZhci1kZWZpbmUsIGxpdmUgPSBUUlVFfQojIERlZmluZSBhIHZhcmlhYmxlIHggdG8gZXF1YWwgNywgYW5kIHByaW50IG91dCB0aGUgdmFsdWUgb2YgeAp4IDwtIDcKCiMgV2UgY2FuIGhhdmUgUiByZXBlYXQgYmFjayB0byB1cyB3aGF0IGB4YCBpcyBieSBqdXN0IHVzaW5nIGB4YAp4CmBgYAoKU29tZSBmZWF0dXJlcyBvZiB2YXJpYWJsZXMsIGNvbnNpZGVyaW5nIHRoZSBleGFtcGxlIGB4IDwtIDdgOgpFdmVyeSB2YXJpYWJsZSBoYXMgYSAqKm5hbWUqKiwgYSAqKnZhbHVlKiosIGFuZCBhICoqdHlwZSoqLiAKVGhpcyB2YXJpYWJsZSdzIG5hbWUgaXMgYHhgLCBpdHMgdmFsdWUgaXMgYDdgLCBhbmQgaXRzIHR5cGUgaXMgYG51bWVyaWNgICg3IGlzIGEgbnVtYmVyISkuClJlLWRlZmluaW5nIGEgdmFyaWFibGUgd2lsbCBvdmVyd3JpdGUgdGhlIHZhbHVlLgoKYGBge3IgdmFyLXJlZGVmaW5lfQp4IDwtIDUuNQoKeApgYGAKCldlIGNhbiBtb2RpZnkgYW4gZXhpc3RpbmcgdmFyaWFibGUgYnkgcmVhc3NpZ25pbmcgaXQgdG8gaXRzIHNhbWUgbmFtZS4gCkhlcmUgd2UnbGwgYWRkIGAyYCB0byBgeGAgYW5kIHJlYXNzaWduIHRoZSByZXN1bHQgYmFjayB0byBgeGAuIAoKYGBge3IgdmFyLW1vZGlmeSwgbGl2ZSA9IFRSVUV9CnggPC0geCArIDIKCngKYGBgCgojIyMgVmFyaWFibGUgbmFtaW5nIG5vdGU6CkFzIGJlc3QgeW91IGNhbiwgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSB5b3VyIHZhcmlhYmxlIG5hbWVzIGluZm9ybWF0aXZlIChlLmcuIGB4YCBkb2Vzbid0IG1lYW4gYW55dGhpbmcsIGJ1dCBgc2FuZHdpY2hfcHJpY2VgIGlzIG1lYW5pbmdmdWwuLi4gaWYgd2UncmUgdGFsa2luZyBhYm91dCB0aGUgY29zdCBvZiBzYW5kd2ljaGVzLCB0aGF0IGlzLi4pLiAKCiMjIyBDb21tZW50cwoKQXJndWFibHkgdGhlIF9fbW9zdCBpbXBvcnRhbnRfXyBhc3BlY3Qgb2YgeW91ciBjb2RpbmcgaXMgY29tbWVudHM6IFNtYWxsIHBpZWNlcyBvZiBleHBsYW5hdG9yeSB0ZXh0IHlvdSBsZWF2ZSBpbiB5b3VyIGNvZGUgdG8gZXhwbGFpbiB3aGF0IHRoZSBjb2RlIGlzIGRvaW5nIGFuZC9vciBsZWF2ZSBub3RlcyB0byB5b3Vyc2VsZiBvciBvdGhlcnMuIApDb21tZW50cyBhcmUgaW52YWx1YWJsZSBmb3IgY29tbXVuaWNhdGluZyB5b3VyIGNvZGUgdG8gb3RoZXJzLCBidXQgdGhleSBhcmUgbW9zdCBpbXBvcnRhbnQgZm9yICoqRnV0dXJlIFlvdSoqLiAKRnV0dXJlIFlvdSBjb21lcyBpbnRvIGV4aXN0ZW5jZSBhYm91dCBvbmUgc2Vjb25kIGFmdGVyIHlvdSB3cml0ZSBjb2RlLCBhbmQgaGFzIG5vIGlkZWEgd2hhdCBvbiBlYXJ0aCBQYXN0IFlvdSB3YXMgdGhpbmtpbmcuIAoKQ29tbWVudHMgaW4gUiBjb2RlIGFyZSBpbmRpY2F0ZWQgd2l0aCBwb3VuZCBzaWducyAoKmFrYSogaGFzaHRhZ3MsIG9jdG90aG9ycHMpLiBSIHdpbGwgX2lnbm9yZV8gYW55IHRleHQgaW4gYSBsaW5lIGFmdGVyIHRoZSBwb3VuZCBzaWduLCBzbyB5b3UgY2FuIHB1dCB3aGF0ZXZlciB0ZXh0IHlvdSBsaWtlIHRoZXJlLgoKYGBge3IgY29tbWVudHN9CjIyLzcgIyBub3QgcXVpdGUgcGkKCiMgSWYgd2UgbmVlZCBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIHBpLCB3ZSBjYW4gdXNlIEV1bGVyJ3MgZm9ybXVsYQojIFRoaXMgdXNlcyBhdGFuKCksIHdoaWNoIGNhbGN1bGF0ZXMgYXJjdGFuZ2VudC4KMjAgKiBhdGFuKDEvNykgKyA4ICogYXRhbigzLzc5KSAKYGBgCgpIZWxwIG91dCBGdXR1cmUgWW91IGJ5IGFkZGluZyBsb3RzIG9mIGNvbW1lbnRzISAKRnV0dXJlIFlvdSBuZXh0IHdlZWsgdGhpbmtzIFRvZGF5IFlvdSBpcyBhbiBpZGlvdCwgYW5kIHRoZSBvbmx5IHdheSB5b3UgY2FuIGNvbnZpbmNlIEZ1dHVyZSBZb3UgdGhhdCBUb2RheSBZb3UgaXMgcmVhc29uYWJseSBjb21wZXRlbnQgaXMgYnkgYWRkaW5nIGNvbW1lbnRzIGluIHlvdXIgY29kZSBleHBsYWluaW5nIHdoeSBUb2RheSBZb3UgaXMgYWN0dWFsbHkgbm90IHNvIGJhZC4KCiMjIEZ1bmN0aW9ucwpXZSBjYW4gdXNlIHByZS1idWlsdCBjb21wdXRhdGlvbiBtZXRob2RzIGNhbGxlZCAiZnVuY3Rpb25zIiBmb3Igb3RoZXIgb3BlcmF0aW9ucy4gCkZ1bmN0aW9ucyBoYXZlIHRoZSBmb2xsb3dpbmcgZm9ybWF0LCB3aGVyZSB0aGUgX2FyZ3VtZW50XyBpcyB0aGUgaW5mb3JtYXRpb24gd2UgYXJlIHByb3ZpZGluZyB0byB0aGUgZnVuY3Rpb24gZm9yIGl0IHRvIHJ1bi4gCkFuIGV4YW1wbGUgb2YgdGhpcyB3YXMgdGhlIGBhdGFuKClgIGZ1bmN0aW9uIHVzZWQgYWJvdmUuCgpgYGByCmZ1bmN0aW9uX25hbWUoYXJndW1lbnQpCmBgYAoKVG8gbGVhcm4gYWJvdXQgZnVuY3Rpb25zLCB3ZSdsbCBleGFtaW5lIG9uZSBjYWxsZWQgYGxvZygpYCBmaXJzdC4gCgpUbyBrbm93IHdoYXQgYSBmdW5jdGlvbiBkb2VzIGFuZCBob3cgdG8gdXNlIGl0LCB1c2UgdGhlIHF1ZXN0aW9uIG1hcmsgd2hpY2ggd2lsbCByZXZlYWwgZG9jdW1lbnRhdGlvbiBpbiB0aGUgKipoZWxwIHBhbmUqKjogYD9sb2dgCiFbcmhlbHBdKHNjcmVlbnNob3RzL3JoZWxwLWxvZy5wbmcpIAoKVGhlIGRvY3VtZW50YXRpb24gdGVsbHMgdXMgdGhhdCBgbG9nKClgIGlzIGRlcml2ZWQgZnJvbSBge2Jhc2V9YCwgbWVhbmluZyBpdCBpcyBhIGZ1bmN0aW9uIHRoYXQgaXMgcGFydCBvZiBiYXNlIFIuIApJdCBwcm92aWRlcyBhIGJyaWVmIGRlc2NyaXB0aW9uIG9mIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMgYW5kIHNob3dzIHNldmVyYWwgZXhhbXBsZXMgb2YgdG8gaG93IHVzZSBpdC4KCkluIHBhcnRpY3VsYXIsIHRoZSBkb2N1bWVudGF0aW9uIHRlbGxzIHVzIGFib3V0IHdoYXQgYXJndW1lbnQocykgdG8gcHJvdmlkZToKCisgVGhlIGZpcnN0IF9yZXF1aXJlZF8gYXJndW1lbnQgaXMgdGhlIHZhbHVlIHdlJ2QgbGlrZSB0byB0YWtlIHRoZSBsb2cgb2YsIGJ5IGRlZmF1bHQgaXRzIF9uYXR1cmFsIGxvZ18KKyBUaGUgc2Vjb25kIF9vcHRpb25hbF8gYXJndW1lbnQgY2FuIHNwZWNpZnkgYSBkaWZmZXJlbnQgYmFzZSByYXRoZXIgdGhhbiB0aGUgZGVmYXVsdCBgZWAuCgpGdW5jdGlvbnMgYWxzbyBfcmV0dXJuXyB2YWx1ZXMgZm9yIHVzIHRvIHVzZS4gCkluIHRoZSBjYXNlIG9mIGBsb2coKWAsIHRoZSByZXR1cm5lZCB2YWx1ZSBpcyB0aGUgbG9nJ2QgdmFsdWUgdGhlIGZ1bmN0aW9uIGNvbXB1dGVkLgoKYGBge3IgbG9nfQpsb2coNzMpCmBgYAoKSGVyZSB3ZSBjYW4gc3BlY2lmeSBhbiBfYXJndW1lbnRfIG9mIGBiYXNlYCB0byBjYWxjdWxhdGUgbG9nIGJhc2UgMy4gCgpgYGB7ciBsb2czfQpsb2coODEsIGJhc2UgPSAzKQpgYGAKCklmIHdlIGRvbid0IHNwZWNpZnkgdGhlIF9hcmd1bWVudF8gbmFtZXMsIGl0IGFzc3VtZXMgdGhleSBhcmUgaW4gdGhlIG9yZGVyIHRoYXQgYGxvZ2AgZGVmaW5lcyB0aGVtLiAKU2VlIGA/bG9nYCB0byBzZWUgbW9yZSBhYm91dCBpdHMgYXJndW1lbnRzLiAKCmBgYHtyIGxvZzIsIGxpdmUgPSBUUlVFfQpsb2coOCwgMikKYGBgCgpXZSBjYW4gc3dpdGNoIHRoZSBvcmRlciBpZiB3ZSBzcGVjaWZ5IHRoZSBhcmd1bWVudCBuYW1lcy4gCgpgYGB7ciBsb2ctb3JkZXJ9CmxvZyhiYXNlID0gMTAsIHggPSA0MzQyKQpgYGAKCldlIGNhbiBhbHNvIHByb3ZpZGUgdmFyaWFibGVzIGFzIGFyZ3VtZW50cyBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIHJhdyB2YWx1ZXMuIAoKYGBge3IgbG9nLXZhcmlhYmxlfQptZWFuaW5nIDwtIDQyCmxvZyhtZWFuaW5nKQpgYGAKCiMjIFdvcmtpbmcgd2l0aCB2YXJpYWJsZXMKCiMjIyBWYXJpYWJsZSBUeXBlcwoKVmFyaWFibGUgdHlwZXMgaW4gUiBjYW4gc29tZXRpbWVzIGJlIF9jb2VyY2VkXyAoY29udmVydGVkKSBmcm9tIG9uZSB0eXBlIHRvIGFub3RoZXIuCgpgYGB7cn0KIyBEZWZpbmUgYSB2YXJpYWJsZSB3aXRoIGEgbnVtYmVyCnggPC0gMTUKYGBgCgpUaGUgZnVuY3Rpb24gYGNsYXNzKClgIHdpbGwgdGVsbCB1cyB0aGUgdmFyaWFibGUncyB0eXBlLgoKYGBge3J9CmNsYXNzKHgpCmBgYAoKTGV0J3MgY29lcmNlIGl0IHRvIGEgY2hhcmFjdGVyLiAKCmBgYHtyfQp4IDwtIGFzLmNoYXJhY3Rlcih4KQpjbGFzcyh4KQpgYGAKClNlZSBpdCBub3cgaGFzIHF1b3RlcyBhcm91bmQgaXQ/IEl0J3Mgbm93IGEgY2hhcmFjdGVyIGFuZCB3aWxsIGJlaGF2ZSBhcyBzdWNoLgoKYGBge3J9CngKYGBgCgpVc2UgdGhpcyBjaHVuayB0byB0cnkgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgd2l0aCBgeGAsIG5vdyB0aGF0IGl0IGlzIGEgY2hhcmFjdGVyLCB3aGF0IGhhcHBlbnM/IAoKYGBge3IgbGl2ZSA9IFRSVUV9CiMgVHJ5IHRvIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGB4YApgYGAKCkJ1dCB3ZSBjYW4ndCBjb2VyY2UgZXZlcnl0aGluZzoKCmBgYHtyfQojIExldCdzIGNyZWF0ZSBhIGNoYXJhY3RlciB2YXJpYWJsZQp4IDwtICJsb29rIGF0IG15IGNoYXJhY3RlciB2YXJpYWJsZSIKYGBgCgpMZXQncyB0cnkgbWFraW5nIHRoaXMgYSBudW1lcmljIHZhcmlhYmxlOgoKYGBge3IgY29lcmNlLWNoYXIsIGVycm9yPVRSVUV9CnggPC0gYXMubnVtZXJpYyh4KQpgYGAKClByaW50IG91dCBgeGAuCgpgYGB7cn0KeApgYGAKClIgaXMgdGVsbGluZyB1cyBpdCBkb2Vzbid0IGtub3cgaG93IHRvIGNvbnZlcnQgdGhpcyB0byBhIG51bWVyaWMgdmFyaWFibGUsIHNvIGl0IGhhcyByZXR1cm5lZCBgTkFgIGluc3RlYWQuCgpGb3IgcmVmZXJlbmNlLCBoZXJlJ3MgYSBzdW1tYXJ5IG9mIHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlIHR5cGVzLiAKCnwgVmFyaWFibGUgVHlwZSB8IERlZmluaXRpb24gfCBFeGFtcGxlcyB8IENvZXJjaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwgLS0tLS0tLS18CnwgYG51bWVyaWNgICAgICAgIHwgQW55IG51bWJlciB2YWx1ZSB8IGA1YDxicj5gNy41YCA8YnI+YC0xYHwgYGFzLm51bWVyaWMoKWAKfCBgaW50ZWdlcmAgICAgICAgfCBBbnkgX3dob2xlXyBudW1iZXIgdmFsdWUgKG5vIGRlY2ltYWxzKSB8IGA1YCA8YnI+IGAtMTAwYCB8IGBhcy5pbnRlZ2VyKClgCnxgY2hhcmFjdGVyYCAgICAgIHwgQW55IGNvbGxlY3Rpb24gb2YgY2hhcmFjdGVycyBkZWZpbmVkIHdpdGhpbiBfcXVvdGF0aW9uIG1hcmtzXy4gQWxzbyBrbm93biBhcyBhICJzdHJpbmciLiB8IGAiYSJgIChhIHNpbmdsZSBsZXR0ZXIpIDxicj5gInN0cmluZ29mbGV0dGVycyJgIChhIHdob2xlIGJ1bmNoIG9mIGNoYXJhY3RlcnMgcHV0IHRvZ2V0aGVyIGFzIG9uZSkgPGJyPiBgInN0cmluZyBvZiBsZXR0ZXJzIGFuZCBzcGFjZXMiYCA8YnI+IGAiNSJgIDxicj4gYCdzaW5nbGUgcXVvdGVzIGFyZSBhbHNvIGdvb2QnYCB8IGBhcy5jaGFyYWN0ZXIoKWAKfGBsb2dpY2FsYCAgICAgIHwgQSB2YWx1ZSBvZiBgVFJVRWAsIGBGQUxTRWAsIG9yIGBOQWAgfCBgVFJVRWAgPGJyPiBgRkFMU0VgIDxicj4gYE5BYCAobm90IGRlZmluZWQpIHwgYGFzLmxvZ2ljYWwoKWAgCnxgZmFjdG9yYCAgICAgICB8IEEgc3BlY2lhbCB0eXBlIG9mIHZhcmlhYmxlIHRoYXQgZGVub3RlcyBzcGVjaWZpYyBjYXRlZ29yaWVzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgfCAoc3RheSB0dW5lZC4uKSB8IGBhcy5mYWN0b3IoKWAKCiMjIyBWZWN0b3JzCgpZb3Ugd2lsbCBoYXZlIG5vdGljZWQgdGhhdCBhbGwgeW91ciBjb21wdXRhdGlvbnMgdGVuZCB0byBwb3AgdXAgd2l0aCBhIGBbMV1gIHByZWNlZGluZyB0aGVtIGluIFIncyBvdXRwdXQuIApUaGlzIGlzIGJlY2F1c2UsIGluIGZhY3QsIGFsbCAob2sgbW9zdGx5IGFsbCkgdmFyaWFibGVzIGFyZSBfYnkgZGVmYXVsdF8gIHZlY3RvcnMsIGFuZCBvdXIgYW5zd2VycyBhcmUgdGhlIGZpcnN0IChpbiB0aGVzZSBjYXNlcyBvbmx5KSB2YWx1ZSBpbiB0aGUgdmVjdG9yLiAKQXMgdmVjdG9ycyBnZXQgbG9uZ2VyLCBuZXcgaW5kZXggaW5kaWNhdG9ycyB3aWxsIGFwcGVhciBhdCB0aGUgc3RhcnQgb2YgbmV3IGxpbmVzLiAKCmBgYHtyfQojIFRoaXMgaXMgYWN0dWFsbHkgYW4gdmVjdG9yIHRoYXQgaGFzIG9uZSBpdGVtIGluIGl0Lgp4IDwtIDcKYGBgCgpgYGB7ciB2ZWN0b3ItbGVuZ3RofQojIFRoZSBsZW5ndGgoKSBmdW5jdGlvbnMgdGVsbHMgdXMgaG93IGxvbmcgYW4gdmVjdG9yIGlzOgpsZW5ndGgoeCkKYGBgCgpXZSBjYW4gZGVmaW5lIHZlY3RvcnMgd2l0aCB0aGUgZnVuY3Rpb24gYGMoKWAsIHdoaWNoIHN0YW5kcyBmb3IgImNvbWJpbmUiLiAKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIGNvbW1hLXNlcGFyYXRlZCBzZXQgb2YgdmFsdWVzIHRvIHBsYWNlIGluIHRoZSB2ZWN0b3IsIGFuZCByZXR1cm5zIHRoZSB2ZWN0b3IgaXRzZWxmOgoKYGBge3IgbWFrZS12ZWN0b3J9Cm15X251bWVyaWNfdmVjdG9yIDwtIGMoMSwgMSwgMiwgMywgNSwgOCwgMTMsIDIxKQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBidWlsZCBvbiB2ZWN0b3JzIGluIHBsYWNlIGJ5IHJlZGVmaW5pbmcgdGhlbToKCmBgYHtyIGZpYmJvbmFjY2ksIGxpdmUgPSBUUlVFfQojIGFkZCB0aGUgbmV4dCB0d28gRmlib25hY2NpIG51bWJlcnMgdG8gdGhlIHNlcmllcy4KbXlfbnVtZXJpY192ZWN0b3IgPC0gYyhteV9udW1lcmljX3ZlY3RvciwgMzQsIDU1KQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBwdWxsIG91dCBzcGVjaWZpYyBpdGVtcyBmcm9tIGFuIHZlY3RvciB1c2luZyBhIHByb2Nlc3MgY2FsbGVkIF9pbmRleGluZ18sIHdoaWNoIHVzZXMgYnJhY2tldHMgYFtdYCB0byBzcGVjaWZ5IHRoZSBwb3NpdGlvbiBvZiBhbiBpdGVtLiAKCmBgYHtyIHN1YnNldDF9CiMgR3JhYiB0aGUgZm91cnRoIHZhbHVlIGZyb20gbXlfbnVtZXJpY192ZWN0b3IKIyBUaGlzIGdpdmVzIHVzIGFuIHZlY3RvciBvZiBsZW5ndGggMSAKbXlfbnVtZXJpY192ZWN0b3JbNF0KYGBgCgpDb2xvbnMgYXJlIGFsc28gYSBuaWNlIHdheSB0byBxdWlja2x5IG1ha2Ugb3JkZXJlZCBudW1lcmljIHZlY3RvcnMKVXNlIGEgY29sb24gdG8gc3BlY2lmeSBhbiBpbmNsdXNpdmUgcmFuZ2Ugb2YgaW5kaWNlcwpUaGlzIHdpbGwgcmV0dXJuIGFuIHZlY3RvciB3aXRoIDIsIDMsIDQsIGFuZCA1LgoKYGBge3Igc3Vic2V0LW1hbnl9Cm15X251bWVyaWNfdmVjdG9yWzI6NV0KYGBgCgpPbmUgbWFqb3IgYmVuZWZpdCBvZiB2ZWN0b3JzIGlzIHRoZSBjb25jZXB0IG9mICoqdmVjdG9yaXphdGlvbioqLCB3aGVyZSBSIGJ5IGRlZmF1bHQgcGVyZm9ybXMgb3BlcmF0aW9ucyBvbiB0aGUgX2VudGlyZSB2ZWN0b3IgYXQgb25jZV8uIApGb3IgZXhhbXBsZSwgd2UgY2FuIGdldCB0aGUgbG9nIG9mIGFsbCBudW1iZXJzIDEtMjAgd2l0aCBhIHNpbmdsZSwgc2ltcGxlIGNhbGwsIGFuZCBtb3JlIQoKYGBge3IgdmVjdG9yaXplfQp2YWx1ZXNfMV90b18yMCA8LSAxOjIwCmBgYAoKCmBgYHtyIHZlY3Rvcml6ZS1sb2csIGxpdmUgPSBUUlVFfQojIGNhbGN1bGF0ZSB0aGUgbG9nIG9mIHZhbHVlc18xX3RvXzIwCmxvZyh2YWx1ZXNfMV90b18yMCkKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gYXBwbHkgbG9naWNhbCBleHByZXNzaW9ucyB0byB2ZWN0b3JzLCBqdXN0IGFzIHdlIGNhbiBkbyBmb3Igc2luZ2xlIHZhbHVlcy4KVGhlIG91dHB1dCBoZXJlIGlzIGEgbG9naWNhbCB2ZWN0b3IgdGVsbGluZyB1cyB3aGV0aGVyIGVhY2ggdmFsdWUgaW4gZXhhbXBsZV92ZWN0b3IgaXMgVFJVRSBvciBGQUxTRQoKYGBge3IgdmVjdG9yLWNvbXBhcmV9CiMgV2hpY2ggdmFsdWVzIGFyZSA8PSAzPwp2YWx1ZXNfMV90b18yMCA8PSAzCmBgYAoKVGhlcmUgYXJlIHNldmVyYWwga2V5IGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgdXNlZCBvbiB2ZWN0b3JzIGNvbnRhaW5pbmcgbnVtZXJpYyB2YWx1ZXMsIHNvbWUgb2Ygd2hpY2ggYXJlIGJlbG93LgoKKyBgbWVhbigpYDogVGhlIGF2ZXJhZ2UgdmFsdWUgaW4gdGhlIHZlY3RvcgorIGBtaW4oKWA6IFRoZSBtaW5pbXVtIHZhbHVlIGluIHRoZSB2ZWN0b3IKKyBgbWF4KClgOiBUaGUgbWF4aW11bSB2YWx1ZSBpbiB0aGUgdmVjdG9yCisgYHN1bSgpYDogVGhlIHN1bSBvZiBhbGwgdmFsdWVzIGluIHRoZSB2ZWN0b3IKCldlIGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyBvbiB0aGUgdmVjdG9yIGB2YWx1ZXNfMV90b18yMGAgd2UndmUgY3JlYXRlZC4gCgpgYGB7ciB2ZWN0b3ItZnVuY3N9Cm1lYW4odmFsdWVzXzFfdG9fMjApCgojIFRyeSBvdXQgc29tZSBvZiB0aGUgb3RoZXIgZnVuY3Rpb25zIHdlJ3ZlIGxpc3RlZCBhYm92ZSAKCmBgYAoKIyMjIEEgbm90ZSBvbiB2YXJpYWJsZSBuYW1pbmcKCldlIGhhdmUgbGVhcm5lZCBmdW5jdGlvbnMgc3VjaCBhcyBgY2AsIGBsZW5ndGhgLCBgc3VtYCwgYW5kIGV0Yy4gCkltYWdpbmUgZGVmaW5pbmcgYSB2YXJpYWJsZSBjYWxsZWQgYGNgOiBUaGlzIHdpbGwgd29yaywgYnV0IGl0IHdpbGwgbGVhZCB0byBhIApsb3Qgb2YgdW5pbnRlbmRlZCBidWdzLCBzbyBpdCdzIGJlc3QgdG8gYXZvaWQgdGhpcy4gCgojIyMgVGhlIGAlaW4lYCBsb2dpY2FsIG9wZXJhdG9yIAoKYCVpbiVgIGlzIHVzZWZ1bCBmb3IgZGV0ZXJtaW5pbmcgd2hldGhlciBhIGdpdmVuIGl0ZW0ocykgYXJlIGluIGFuIHZlY3Rvci4KCmBgYHtyIGluLW9wZXJhdG9yfQojIGlzIGA3YCBpbiBvdXIgdmVjdG9yPyAKNyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKYGBge3IgaW4yLCBsaXZlID0gVFJVRX0KIyBpcyBgNTBgIGluIG91ciB2ZWN0b3I/IAo1MCAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKV2UgY2FuIHRlc3QgYSB2ZWN0b3Igb2YgdmFsdWVzIGJlaW5nIHdpdGhpbiBhbm90aGVyIHZlY3RvciBvZiB2YWx1ZXMuIAoKYGBge3IgdmVjdG9yLWluLCBsaXZlID0gVFJVRX0KcXVlc3Rpb25fdmFsdWVzIDwtIGMoMTozLCA3LCA1MCkKIyBBcmUgdGhlc2UgdmFsdWVzIGluIG91ciB2ZWN0b3I/CnF1ZXN0aW9uX3ZhbHVlcyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKIyMgRGF0YSBmcmFtZXMKCl9EYXRhIGZyYW1lcyBhcmUgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBmb3IgZGF0YSBhbmFseXNpcyBpbiBSLl8gClRoZXkgYXJlIHRhYmxlcyB3aGljaCBjb25zaXN0IG9mIHJvd3MgYW5kIGNvbHVtbnMsIG11Y2ggbGlrZSBhIF9zcHJlYWRzaGVldF8uIApFYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlIHdoaWNoIGJlaGF2ZXMgYXMgYSBfdmVjdG9yXywgYW5kIGVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uLiAKV2Ugd2lsbCBiZWdpbiBvdXIgZXhwbG9yYXRpb24gd2l0aCBkYXRhc2V0IG9mIG1lYXN1cmVtZW50cyBmcm9tIHRocmVlIHBlbmd1aW4gc3BlY2llcyBtZWFzdXJlZCwgd2hpY2ggd2UgY2FuIGZpbmQgaW4gdGhlIFtgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2VdKGh0dHBzOi8vYWxsaXNvbmhvcnN0LmdpdGh1Yi5pby9wYWxtZXJwZW5ndWlucy8pLiAKV2UnbGwgdGFsayBtb3JlIGFib3V0IHBhY2thZ2VzIHNvb24hClRvIHVzZSB0aGlzIGRhdGFzZXQsIHdlIHdpbGwgbG9hZCBpdCBmcm9tIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UgdXNpbmcgYSBgOjpgIChtb3JlIG9uIHRoaXMgbGF0ZXIpIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lZCBgcGVuZ3VpbnNgIGluIG91ciBjdXJyZW50IGVudmlyb25tZW50LgoKYGBge3IgcGVuZ3Vpbi1saWJyYXJ5fQpwZW5ndWlucyA8LSBwYWxtZXJwZW5ndWluczo6cGVuZ3VpbnMKYGBgCgohW2RyYXdpbmdzIG9mIHBlbmd1aW4gc3BlY2llc10oZGlhZ3JhbXMvbHRlcl9wZW5ndWlucy5wbmcpIEFydHdvcmsgYnkgW0BhbGxpc29uX2hvcnN0XShodHRwczovL3R3aXR0ZXIuY29tL2FsbGlzb25faG9yc3QpCgojIyMgRXhwbG9yaW5nIGRhdGEgZnJhbWVzCgpUaGUgZmlyc3Qgc3RlcCB0byB1c2luZyBhbnkgZGF0YSBpcyB0byBsb29rIGF0IGl0ISEhIApSU3R1ZGlvIGNvbnRhaW5zIGEgc3BlY2lhbCBmdW5jdGlvbiBgVmlldygpYCB3aGljaCBhbGxvd3MgeW91IHRvIGxpdGVyYWxseSB2aWV3IGEgdmFyaWFibGUuCllvdSBjYW4gYWxzbyBjbGljayBvbiB0aGUgb2JqZWN0IGluIHRoZSBlbnZpcm9ubWVudCBwYW5lIHRvIHNlZSBpdHMgb3ZlcmFsbCBwcm9wZXJ0aWVzLCBvciBjbGljayB0aGUgdGFibGUgaWNvbiBvbiB0aGUgb2JqZWN0J3Mgcm93IHRvIGF1dG9tYXRpY2FsbHkgdmlldyB0aGUgdmFyaWFibGUuIAoKU29tZSB1c2VmdWwgZnVuY3Rpb25zIGZvciBleHBsb3Jpbmcgb3VyIGRhdGEgZnJhbWUgaW5jbHVkZToKCisgYGhlYWQoKWAgdG8gc2VlIHRoZSBmaXJzdCA2IHJvd3Mgb2YgYSBkYXRhIGZyYW1lLiBBZGRpdGlvbmFsIGFyZ3VtZW50cyBzdXBwbGllZCBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2Ygcm93cy4KKyBgdGFpbCgpYCB0byBzZWUgdGhlIGxhc3QgNiByb3dzIG9mIGEgZGF0YSBmcmFtZS4gQWRkaXRpb25hbCBhcmd1bWVudHMgc3VwcGxpZWQgY2FuIGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MuCisgYG5hbWVzKClgIHRvIHNlZSB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBkYXRhIGZyYW1lLgorIGBucm93KClgIHRvIHNlZSBob3cgbWFueSByb3dzIGFyZSBpbiB0aGUgZGF0YSBmcmFtZQorIGBuY29sKClgIHRvIHNlZSBob3cgbWFueSBjb2x1bW5zIGFyZSBpbiB0aGUgZGF0YSBmcmFtZS4KCldlIGNhbiBhZGRpdGlvbmFsbHkgZXhwbG9yZSBfb3ZlcmFsbCBwcm9wZXJ0aWVzXyBvZiB0aGUgZGF0YSBmcmFtZSB3aXRoIHR3byBkaWZmZXJlbnQgZnVuY3Rpb25zOiBgc3VtbWFyeSgpYCBhbmQgYHN0cigpYC4KClRoaXMgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGNvbHVtbjoKCmBgYHtyIHBlbmd1aW5zLXN1bW1hcnl9CnN1bW1hcnkocGVuZ3VpbnMpCmBgYAoKVGhpcyBwcm92aWRlcyBhIHNob3J0IHZpZXcgb2YgdGhlICoqc3RyKip1Y3R1cmUgYW5kIGNvbnRlbnRzIG9mIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgcGVuZ3VpbnMtc3RyfQpzdHIocGVuZ3VpbnMpCmBgYAoKWW91J2xsIG5vdGljZSB0aGF0IHRoZSBjb2x1bW4gYHNwZWNpZXNgIGlzIGEgX2ZhY3Rvcl86IFRoaXMgaXMgYSBzcGVjaWFsIHR5cGUgb2YgY2hhcmFjdGVyIHZhcmlhYmxlIHRoYXQgcmVwcmVzZW50cyBkaXN0aW5jdCBjYXRlZ29yaWVzIGtub3duIGFzICJsZXZlbHMiLiAKV2UgaGF2ZSBsZWFybmVkIGhlcmUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgbGV2ZWxzIGluIHRoZSBgc3BlY2llc2AgY29sdW1uOiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvby4KV2UgbWlnaHQgd2FudCB0byBleHBsb3JlIGluZGl2aWR1YWwgY29sdW1ucyBvZiB0aGUgZGF0YSBmcmFtZSBtb3JlIGluLWRlcHRoLiAKV2UgY2FuIGV4YW1pbmUgaW5kaXZpZHVhbCBjb2x1bW5zIHVzaW5nIHRoZSBkb2xsYXIgc2lnbiBgJGAgdG8gc2VsZWN0IG9uZSBieSBuYW1lOgoKYGBge3IgcGVuZ3VpbnMtc3Vic2V0fQojIEV4dHJhY3QgYmlsbF9sZW5ndGhfbW0gYXMgYSB2ZWN0b3IKcGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0KCiMgaW5kZXhpbmcgb3BlcmF0b3JzIGNhbiBiZSB1c2VkIG9uIHRoZXNlIHZlY3RvcnMgdG9vCnBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tWzE6MTBdCmBgYAoKV2UgY2FuIHBlcmZvcm0gb3VyIHJlZ3VsYXIgdmVjdG9yIG9wZXJhdGlvbnMgb24gY29sdW1ucyBkaXJlY3RseS4KCmBgYHtyIHBlbmd1aW5zLWNvbC1tZWFuLCBsaXZlID0gVFJVRX0KIyBjYWxjdWxhdGUgdGhlIG1lYW4gb2YgdGhlIGJpbGxfbGVuZ3RoX21tIGNvbHVtbgptZWFuKHBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tLAogICAgIG5hLnJtID0gVFJVRSkgIyByZW1vdmUgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBtZWFuCmBgYAoKV2UgY2FuIGFsc28gY2FsY3VsYXRlIHRoZSBmdWxsIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBzaW5nbGUgY29sdW1uIGRpcmVjdGx5LiAKCmBgYHtyIHBlbmd1aW5zLWNvbC1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzaG93IGEgc3VtbWFyeSBvZiB0aGUgYmlsbF9sZW5ndGhfbW0gY29sdW1uCnN1bW1hcnkocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0pCmBgYAoKRXh0cmFjdCBgc3BlY2llc2AgYXMgYSB2ZWN0b3IgYW5kIHN1YnNldCBpdCB0byBzZWUgYSBwcmV2aWV3LgoKYGBge3IgcGVuZ3VpbnMtY29sLXN1YnNldCwgbGl2ZSA9IFRSVUV9CiMgZ2V0IHRoZSBmaXJzdCAxMCB2YWx1ZXMgb2YgdGhlIHNwZWNpZXMgY29sdW1uCnBlbmd1aW5zJHNwZWNpZXNbMToxMF0KYGBgCgpBbmQgdmlldyBpdHMgX2xldmVsc18gd2l0aCB0aGUgYGxldmVscygpYCBmdW5jdGlvbi4KCmBgYHtyIHBlbmd1aW4tbGV2ZWxzfQpsZXZlbHMocGVuZ3VpbnMkc3BlY2llcykKYGBgCgojIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCkluIG1hbnkgc2l0dWF0aW9ucywgd2Ugd2lsbCBiZSByZWFkaW5nIGluIHRhYnVsYXIgZGF0YSBmcm9tIGEgZmlsZSBhbmQgdXNpbmcgaXQgYXMgYSBkYXRhIGZyYW1lLiAKVG8gcHJhY3RpY2UsIHdlIHdpbGwgcmVhZCBpbiBhIGZpbGUgd2Ugd2lsbCBiZSB1c2luZyBpbiB0aGUgbmV4dCBub3RlYm9vayBhcyB3ZWxsLCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAsIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gdGhlIGxvY2F0aW9uIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuCgpIZXJlIHdlIHdpbGwgdXNlIGEgZnVuY3Rpb24sIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkJlZm9yZSB3ZSBhcmUgYWJsZSB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBoYXZlIHRvIGxvYWQgdGhlIHBhY2thZ2UgdXNpbmcgYGxpYnJhcnkoKWAuIAoKYGBge3IgcmVhZHJ9CmxpYnJhcnkocmVhZHIpCmBgYAoKYGZpbGUucGF0aCgpYCBjcmVhdGVzIGEgcHJvcGVybHkgZm9ybWF0dGVkIGZpbGUgcGF0aCBieSBhZGRpbmcgYSBwYXRoIHNlcGFyYXRvciAoYC9gIG9uIE1hYyBhbmQgTGludXggb3BlcmF0aW5nIHN5c3RlbXMsIHRoZSBsYXR0ZXIgb2Ygd2hpY2ggaXMgdGhlIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCBvdXIgUlN0dWRpbyBTZXJ2ZXIgcnVucyBvbikgYmV0d2VlbiBzZXBhcmF0ZSBmb2xkZXJzIG9yIGRpcmVjdG9yaWVzLgpCZWNhdXNlIGZpbGUgcGF0aCBzZXBhcmF0b3JzIGNhbiBkaWZmZXIgYmV0d2VlbiB5b3VyIGNvbXB1dGVyIGFuZCB0aGUgY29tcHV0ZXIgb2Ygc29tZW9uZSB3aG8gd2FudHMgdG8gdXNlIHlvdXIgY29kZSwgd2UgdXNlIGBmaWxlLnBhdGgoKWAgaW5zdGVhZCBvZiB0eXBpbmcgb3V0IGAiZGF0YS9nZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2ImAuCkVhY2ggX2FyZ3VtZW50XyB0byBgZmlsZS5wYXRoKClgIGlzIGEgZGlyZWN0b3J5IG9yIGZpbGUgbmFtZS4KWW91J2xsIG5vdGljZSBlYWNoIGFyZ3VtZW50IGlzIGluIHF1b3Rlcywgd2Ugc3BlY2lmeSBgZGF0YWAgZmlyc3QgYmVjYXVzZSB0aGUgZmlsZSwgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGlzIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKCmBgYHtyIGZpbGUucGF0aH0KZmlsZS5wYXRoKCJkYXRhIiwgImdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3YiKQpgYGAKCkFzIHlvdSBjYW4gc2VlIGFib3ZlLCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgYGZpbGUucGF0aCgpYCBpcyB0aGF0IGl0IF9jcmVhdGVzIGEgc3RyaW5nXyB3aXRoIGFuIGFjY3VyYXRlbHktZm9ybWF0dGVkIHBhdGggZm9yIHlvdXIgZmlsZSBzeXN0ZW0uClRoaXMgc3RyaW5nIGNhbiBiZSB1c2VkIG1vdmluZyBmb3J3YXJkIHdoZW4geW91IG5lZWQgdG8gcmVmZXIgdG8gdGhlIHBhdGggdG8geW91ciBmaWxlLgpMZXQncyBnbyBhaGVhZCBhbmQgc3RvcmUgdGhpcyBmaWxlIHBhdGggYXMgYSB2YXJpYWJsZSBpbiBvdXIgZW52aXJvbm1lbnQuIAoKYGBge3IgZmlsZS5wYXRoLXZhcmlhYmxlfQpnZW5lX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIpCmBgYAoKTm93IHdlIGFyZSByZWFkeSB0byB1c2UgYHJlYWRfdHN2KClgIHRvIHJlYWQgdGhlIGZpbGUgaW50byBSLgpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2lsbCBiZSBzdG9yZWQgaW4gYSB2YXJpYWJsZSBuYW1lZCBgc3RhdHNfZGZgLgpOb3RlIHRoZSBgPC1gIChhc3NpZ25tZW50IG9wZXJhdG9yISkgaXMgcmVzcG9uc2libGUgZm9yIHNhdmluZyB0aGlzIHRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuIAoKYGBge3IgcmVhZC1zdGF0c30KIyByZWFkIGluIHRoZSBmaWxlIGBnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2YCBmcm9tIHRoZSBkYXRhIGRpcmVjdG9yeQpzdGF0c19kZiA8LSByZWFkX3RzdihnZW5lX2ZpbGVfcGF0aCkKYGBgCgpUYWtlIGEgbG9vayBhdCB5b3VyIGVudmlyb25tZW50IHBhbmVsIHRvIHNlZSB3aGF0IGBzdGF0c19kZmAgbG9va3MgbGlrZS4gCldlIGNhbiBhbHNvIHByaW50IG91dCBhIHByZXZpZXcgb2YgdGhlIGBzdGF0c19kZmAgZGF0YSBmcmFtZSBoZXJlLiAKCmBgYHtyIHNob3ctc3RhdHMsIGxpdmUgPSBUUlVFfQojIGRpc3BsYXkgc3RhdHNfZGYKc3RhdHNfZGYKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpBdCB0aGUgZW5kIG9mIGV2ZXJ5IG5vdGVib29rLCB5b3Ugd2lsbCBzZWUgdXMgcHJpbnQgb3V0IGBzZXNzaW9uSW5mb2AuIApUaGlzIGFpZHMgaW4gdGhlIHJlcHJvZHVjaWJpbGl0eSBvZiB5b3VyIGNvZGUgYnkgc2hvd2luZyBleGFjdGx5IHdoYXQgcGFja2FnZXMgCmFuZCB2ZXJzaW9ucyB3ZXJlIGJlaW5nIHVzZWQgdGhlIGxhc3QgdGltZSB0aGUgbm90ZWJvb2sgd2FzIHJ1bi4KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=
+LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgYW5kIFJTdHVkaW8iCmF1dGhvcjogT3JpZ2luYWxseSBhdXRob3JlZCBieSBTdGVwaGFuaWUgSi4gU3BpZWxtYW4sPGJyPmFkYXB0ZWQgYnkgQ0NETCBmb3IgQUxTRgpkYXRlOiAyMDIxCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOiAgCgotIE5hdmlnYXRlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50ICAKLSBVc2UgUiBmb3Igc2ltcGxlIGNhbGN1bGF0aW9ucywgYm90aCBtYXRoZW1hdGljYWwgYW5kIGxvZ2ljYWwgIAotIERlZmluZSBhbmQgdXNlIHZhcmlhYmxlcyBpbiBiYXNlIFIgIAotIFVuZGVyc3RhbmQgYW5kIGFwcGx5IGJhc2UgUiBmdW5jdGlvbnMgICAKLSBVbmRlcnN0YW5kLCBkZWZpbmUsIGFuZCB1c2UgUiBkYXRhIHR5cGVzLCBpbmNsdWRpbmcgdmVjdG9yIG1hbmlwdWxhdGlvbiBhbmQgaW5kZXhpbmcgIAotIFVuZGVyc3RhbmQgdGhlIGFuYXRvbXkgb2YgYSBkYXRhIGZyYW1lICAKCi0tLQoKIyMjIyAqTW9yZSByZXNvdXJjZXMgZm9yIGxlYXJuaW5nIFIqIAoKLSBbU3dpcmwsIGFuIGludGVyYWN0aXZlIHR1dG9yaWFsXShodHRwczovL3N3aXJsc3RhdHMuY29tLykgIAotIFtfUiBmb3IgRGF0YSBTY2llbmNlXyBib29rXShodHRwczovL3I0ZHMuaGFkbGV5Lm56LykgIAotIFtUdXRvcmlhbCBvbiBSLCBSU3R1ZGlvIGFuZCBSIE1hcmtkb3duXShodHRwczovL2lzbWF5Yy5naXRodWIuaW8vcmJhc2ljcy1ib29rLykgIAotIFtIYW5keSBSIGNoZWF0c2hlZXRzXShodHRwczovL3d3dy5wb3NpdC5jby9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAgCi0gW1IgTWFya2Rvd24gd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pICAKLSBbX1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlX10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgIAoKIyMgV2hhdCBpcyBSPwoKKipSKiogaXMgYSBzdGF0aXN0aWNhbCBjb21wdXRpbmcgbGFuZ3VhZ2UgdGhhdCBpcyBfb3BlbiBzb3VyY2VfLCBtZWFuaW5nIHRoZSB1bmRlcmx5aW5nIGNvZGUgZm9yIHRoZSBsYW5ndWFnZSBpcyBmcmVlbHkgYXZhaWxhYmxlIHRvIGFueW9uZS4gCllvdSBkbyBub3QgbmVlZCBhIHNwZWNpYWwgbGljZW5zZSBvciBzZXQgb2YgcGVybWlzc2lvbnMgdG8gdXNlIGFuZCBkZXZlbG9wIGNvZGUgaW4gUi4gCgpSIGl0c2VsZiBpcyBhbiBfaW50ZXJwcmV0ZWQgY29tcHV0ZXIgbGFuZ3VhZ2VfIGFuZCBjb21lcyB3aXRoIGZ1bmN0aW9uYWxpdHkgdGhhdCBjb21lcyBidW5kbGVkIHdpdGggdGhlIGxhbmd1YWdlIGl0c2VsZiwga25vd24gYXMgKioiYmFzZSBSIioqLgpCdXQgdGhlcmUgaXMgYWxzbyByaWNoIGFkZGl0aW9uYWwgZnVuY3Rpb25hbGl0eSBwcm92aWRlZCBieSAqKmV4dGVybmFsIHBhY2thZ2VzKiosIG9yIGxpYnJhcmllcyBvZiBjb2RlIHRoYXQgYXNzaXN0IGluIGFjY29tcGxpc2hpbmcgY2VydGFpbiB0YXNrcyBhbmQgY2FuIGJlIGZyZWVseSBkb3dubG9hZGVkIGFuZCBsb2FkZWQgZm9yIHVzZS4gCgpJbiB0aGUgbmV4dCBub3RlYm9vayBhbmQgc3Vic2VxdWVudCBtb2R1bGVzLCB3ZSB3aWxsIGJlIHVzaW5nIGEgc3VpdGUgb2YgcGFja2FnZXMgY29sbGVjdGl2ZWx5IGtub3duIGFzIFsqKlRoZSBUaWR5dmVyc2UqKl0oaHR0cHM6Ly90aWR5dmVyc2Uub3JnKS4gClRoZSBgdGlkeXZlcnNlYCBpcyBnZWFyZWQgdG93YXJkcyBpbnR1aXRpdmUgZGF0YSBzY2llbmNlIGFwcGxpY2F0aW9ucyB0aGF0IGZvbGxvdyBhIHNoYXJlZCBkYXRhIHBoaWxvc29waHkuCkJ1dCB0aGVyZSBhcmUgc3RpbGwgbWFueSBjb3JlIGZlYXR1cmVzIG9mIGJhc2UgUiB3aGljaCBhcmUgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mLCBhbmQgd2Ugd2lsbCBiZSB1c2luZyBjb25jZXB0cyBmcm9tIGJvdGggYmFzZSBSIGFuZCB0aGUgdGlkeXZlcnNlIGluIG91ciBhbmFseXNlcywgYXMgd2VsbCBhcyB0YXNrIHNwZWNpZmljIHBhY2thZ2VzIGZvciBhbmFseXNlcyBzdWNoIGFzIGdlbmUgZXhwcmVzc2lvbi4gCgojIyMgV2hhdCBpcyBSU3R1ZGlvPwoKUlN0dWRpbyBpcyBhIF9ncmFwaGljYWwgZW52aXJvbm1lbnRfICgiaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCIgb3IgSURFKSBmb3Igd3JpdGluZyBhbmQgZGV2ZWxvcGluZyBSIGNvZGUuIFJTdHVkaW8gaXMgTk9UIGEgc2VwYXJhdGUgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgLSBpdCBpcyBhbiBpbnRlcmZhY2Ugd2UgdXNlIHRvIGZhY2lsaXRhdGUgUiBwcm9ncmFtbWluZy4gCkluIG90aGVyIHdvcmRzLCB5b3UgY2FuIHByb2dyYW0gaW4gUiB3aXRob3V0IFJTdHVkaW8sIGJ1dCB5b3UgY2FuJ3QgdXNlIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50IHdpdGhvdXQgUi4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IFJTdHVkaW8gdGhhbiB5b3UgZXZlciB3YW50ZWQgdG8ga25vdywgc2VlIHRoaXMgW1JTdHVkaW8gSURFIENoZWF0c2hlZXQgKHBkZildKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYWluL3JzdHVkaW8taWRlLnBkZikuCgojIyBUaGUgUlN0dWRpbyBFbnZpcm9ubWVudAoKVGhlIFJTdHVkaW8gZW52aXJvbm1lbnQgaGFzIGZvdXIgbWFpbiAqKnBhbmVzKiosIGVhY2ggb2Ygd2hpY2ggbWF5IGhhdmUgYSBudW1iZXIgb2YgdGFicyB0aGF0IGRpc3BsYXkgZGlmZmVyZW50IGluZm9ybWF0aW9uIG9yIGZ1bmN0aW9uYWxpdHkuICh0aGVpciBzcGVjaWZpYyBsb2NhdGlvbiBjYW4gYmUgY2hhbmdlZCB1bmRlciBUb29scyAtPiBHbG9iYWwgT3B0aW9ucyAtPiBQYW5lIExheW91dCkuCiFbUlN0dWRpbyBBcHBlYXJhbmNlXShzY3JlZW5zaG90cy9yc3R1ZGlvLXBhbmVzLnBuZykgCgoxLiBUaGUgKipFZGl0b3IqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gd3JpdGUgUiBzY3JpcHRzIGFuZCBvdGhlciBkb2N1bWVudHMuIEVhY2ggdGFiIGhlcmUgaXMgaXRzIG93biBkb2N1bWVudC4KVGhpcyBpcyB5b3VyIF90ZXh0IGVkaXRvcl8sIHdoaWNoIHdpbGwgYWxsb3cgeW91IHRvIHNhdmUgeW91ciBSIGNvZGUgZm9yIGZ1dHVyZSB1c2UuIApOb3RlIHRoYXQgY2hhbmdlIGNvZGUgaGVyZSB3aWxsIG5vdCBydW4gYXV0b21hdGljYWxseSB1bnRpbCB5b3UgcnVuIGl0LiAKCjIuIFRoZSAqKkNvbnNvbGUqKiBwYW5lIGlzIHdoZXJlIHlvdSBjYW4gX2ludGVyYWN0aXZlbHlfIHJ1biBSIGNvZGUuIAogICsgVGhlcmUgaXMgYWxzbyBhICoqVGVybWluYWwqKiB0YWIgaGVyZSB3aGljaCBjYW4gYmUgdXNlZCBmb3IgcnVubmluZyBwcm9ncmFtcyBvdXRzaWRlIFIgb24geW91ciBjb21wdXRlcgogIAozLiBUaGUgKipFbnZpcm9ubWVudCoqIHBhbmUgcHJpbWFyaWx5IGRpc3BsYXlzIHRoZSB2YXJpYWJsZXMsIHNvbWV0aW1lcyBrbm93biBhcyBfb2JqZWN0c18gdGhhdCBhcmUgZGVmaW5lZCBkdXJpbmcgYSBnaXZlbiBSIHNlc3Npb24sIGFuZCB3aGF0IGRhdGEgb3IgdmFsdWVzIHRoZXkgbWlnaHQgaG9sZC4KCjQuIFRoZSBmaW5hbCBwYW5lLCAqKkZpbGVzLCBQbG90cywgSGVscCwgLi4uKiosIGhhcyBzZXZlcmFsIHByZXR0eSBpbXBvcnRhbnQgdGFiczoKICAgICsgVGhlICoqRmlsZXMqKiB0YWIgc2hvd3MgdGhlIHN0cnVjdHVyZSBhbmQgY29udGVudHMgb2YgZmlsZXMgYW5kIGZvbGRlcnMgKGFsc28ga25vd24gYXMgZGlyZWN0b3JpZXMpIG9uIHlvdXIgY29tcHV0ZXIuCiAgICArIFRoZSAqKlBsb3RzKiogdGFiIHdpbGwgcmV2ZWFsIHBsb3RzIHdoZW4geW91IG1ha2UgdGhlbQogICAgKyBUaGUgKipQYWNrYWdlcyoqIHRhYiBzaG93cyB3aGljaCBpbnN0YWxsZWQgcGFja2FnZXMgaGF2ZSBiZWVuIGxvYWRlZCBpbnRvIHlvdXIgUiBzZXNzaW9uCiAgICArIFRoZSAqKkhlbHAqKiB0YWIgd2lsbCBzaG93IHRoZSBoZWxwIHBhZ2Ugd2hlbiB5b3UgbG9vayB1cCBhIGZ1bmN0aW9uCiAgICArIFRoZSAqKlZpZXdlcioqIHRhYiB3aWxsIHJldmVhbCBjb21waWxlZCBSIE1hcmtkb3duIGRvY3VtZW50cwoKIyMgQmFzaWMgQ2FsY3VsYXRpb25zCgojIyMgTWF0aGVtYXRpY2FsIG9wZXJhdG9ycwoKVGhlIG1vc3QgYmFzaWMgdXNlIG9mIFIgaXMgYXMgYSByZWd1bGFyIGNhbGN1bGF0b3I6Cgp8IE9wZXJhdGlvbiB8IFN5bWJvbCB8CnwtLS0tLS0tLS0tLXwtLS0tLS0tLXwKfCBBZGQgIHwgYCtgIHwgCnwgU3VidHJhY3QgIHwgYC1gIHwgCnwgTXVsdGlwbHkgIHwgYCpgIHwgCnwgRGl2aWRlICB8IGAvYCB8IAp8IEV4cG9uZW50aWF0ZSB8IGBeYCBvciBgKipgIHwgCgpGb3IgZXhhbXBsZSwgd2UgY2FuIGRvIHNvbWUgc2ltcGxlIG11bHRpcGxpY2F0aW9uIGxpa2UgdGhpcy4gCldoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgCnBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKiBvbiBhIE1hYywgb3IgKkN0cmwrU2hpZnQrRW50ZXIqIG9uIGEgUEMuCgpgYGB7ciBjYWxjdWxhdG9yfQo1ICogNgpgYGAKClVzZSB0aGUgY29uc29sZSB0byBjYWxjdWxhdGUgb3RoZXIgZXhwcmVzc2lvbnMuIFN0YW5kYXJkIG9yZGVyIG9mIG9wZXJhdGlvbnMgYXBwbGllcyAobW9zdGx5KSwgYW5kICB5b3UgY2FuIHVzZSBwYXJlbnRoZXNlcyBgKClgIGFzIHlvdSBtaWdodCBleHBlY3QgKGJ1dCBub3QgYnJhY2tldHMgYFtdYCBvciBicmFjZXNge31gLCB3aGljaCBoYXZlIHNwZWNpYWwgbWVhbmluZ3MpLiBOb3RlIGhvd2V2ZXIsIHRoYXQgeW91IG11c3QgKiphbHdheXMqKiBzcGVjaWZ5IG11bHRpcGxpY2F0aW9uIHdpdGggYCpgOyBpbXBsaWNpdCBtdWx0aXBsaWNhdGlvbiBzdWNoIGFzIGAxMCgzICsgNClgIG9yIGAxMHhgIHdpbGwgbm90IHdvcmsgYW5kIHdpbGwgZ2VuZXJhdGUgYW4gZXJyb3IsIG9yIHdvcnNlLgoKYGBge3IgZXhwcmVzc2lvbnMsIGxpdmUgPSBUUlVFfQoxMCAqICgzICsgNCleMgpgYGAKCgojIyMgRGVmaW5pbmcgYW5kIHVzaW5nIHZhcmlhYmxlcyAKClRvIGRlZmluZSBhIHZhcmlhYmxlLCB3ZSB1c2UgdGhlIF9hc3NpZ25tZW50IG9wZXJhdG9yXyB3aGljaCBsb29rcyBsaWtlIGFuIGFycm93OiBgPC1gLCBmb3IgZXhhbXBsZSBgeCA8LSA3YCB0YWtlcyB0aGUgdmFsdWUgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB0aGUgb3BlcmF0b3IgYW5kIGFzc2lnbnMgaXQgdG8gdGhlIHZhcmlhYmxlIG5hbWUgb24gdGhlIGxlZnQtaGFuZCBzaWRlLiAKCmBgYHtyIHZhci1kZWZpbmUsIGxpdmUgPSBUUlVFfQojIERlZmluZSBhIHZhcmlhYmxlIHggdG8gZXF1YWwgNywgYW5kIHByaW50IG91dCB0aGUgdmFsdWUgb2YgeAp4IDwtIDcKCiMgV2UgY2FuIGhhdmUgUiByZXBlYXQgYmFjayB0byB1cyB3aGF0IGB4YCBpcyBieSBqdXN0IHVzaW5nIGB4YAp4CmBgYAoKU29tZSBmZWF0dXJlcyBvZiB2YXJpYWJsZXMsIGNvbnNpZGVyaW5nIHRoZSBleGFtcGxlIGB4IDwtIDdgOgpFdmVyeSB2YXJpYWJsZSBoYXMgYSAqKm5hbWUqKiwgYSAqKnZhbHVlKiosIGFuZCBhICoqdHlwZSoqLiAKVGhpcyB2YXJpYWJsZSdzIG5hbWUgaXMgYHhgLCBpdHMgdmFsdWUgaXMgYDdgLCBhbmQgaXRzIHR5cGUgaXMgYG51bWVyaWNgICg3IGlzIGEgbnVtYmVyISkuClJlLWRlZmluaW5nIGEgdmFyaWFibGUgd2lsbCBvdmVyd3JpdGUgdGhlIHZhbHVlLgoKYGBge3IgdmFyLXJlZGVmaW5lfQp4IDwtIDUuNQoKeApgYGAKCldlIGNhbiBtb2RpZnkgYW4gZXhpc3RpbmcgdmFyaWFibGUgYnkgcmVhc3NpZ25pbmcgaXQgdG8gaXRzIHNhbWUgbmFtZS4gCkhlcmUgd2UnbGwgYWRkIGAyYCB0byBgeGAgYW5kIHJlYXNzaWduIHRoZSByZXN1bHQgYmFjayB0byBgeGAuIAoKYGBge3IgdmFyLW1vZGlmeSwgbGl2ZSA9IFRSVUV9CnggPC0geCArIDIKCngKYGBgCgojIyMgVmFyaWFibGUgbmFtaW5nIG5vdGU6CkFzIGJlc3QgeW91IGNhbiwgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSB5b3VyIHZhcmlhYmxlIG5hbWVzIGluZm9ybWF0aXZlIChlLmcuIGB4YCBkb2Vzbid0IG1lYW4gYW55dGhpbmcsIGJ1dCBgc2FuZHdpY2hfcHJpY2VgIGlzIG1lYW5pbmdmdWwuLi4gaWYgd2UncmUgdGFsa2luZyBhYm91dCB0aGUgY29zdCBvZiBzYW5kd2ljaGVzLCB0aGF0IGlzLi4pLiAKCiMjIyBDb21tZW50cwoKQXJndWFibHkgdGhlIF9fbW9zdCBpbXBvcnRhbnRfXyBhc3BlY3Qgb2YgeW91ciBjb2RpbmcgaXMgY29tbWVudHM6IFNtYWxsIHBpZWNlcyBvZiBleHBsYW5hdG9yeSB0ZXh0IHlvdSBsZWF2ZSBpbiB5b3VyIGNvZGUgdG8gZXhwbGFpbiB3aGF0IHRoZSBjb2RlIGlzIGRvaW5nIGFuZC9vciBsZWF2ZSBub3RlcyB0byB5b3Vyc2VsZiBvciBvdGhlcnMuIApDb21tZW50cyBhcmUgaW52YWx1YWJsZSBmb3IgY29tbXVuaWNhdGluZyB5b3VyIGNvZGUgdG8gb3RoZXJzLCBidXQgdGhleSBhcmUgbW9zdCBpbXBvcnRhbnQgZm9yICoqRnV0dXJlIFlvdSoqLiAKRnV0dXJlIFlvdSBjb21lcyBpbnRvIGV4aXN0ZW5jZSBhYm91dCBvbmUgc2Vjb25kIGFmdGVyIHlvdSB3cml0ZSBjb2RlLCBhbmQgaGFzIG5vIGlkZWEgd2hhdCBvbiBlYXJ0aCBQYXN0IFlvdSB3YXMgdGhpbmtpbmcuIAoKQ29tbWVudHMgaW4gUiBjb2RlIGFyZSBpbmRpY2F0ZWQgd2l0aCBwb3VuZCBzaWducyAoKmFrYSogaGFzaHRhZ3MsIG9jdG90aG9ycHMpLiBSIHdpbGwgX2lnbm9yZV8gYW55IHRleHQgaW4gYSBsaW5lIGFmdGVyIHRoZSBwb3VuZCBzaWduLCBzbyB5b3UgY2FuIHB1dCB3aGF0ZXZlciB0ZXh0IHlvdSBsaWtlIHRoZXJlLgoKYGBge3IgY29tbWVudHN9CjIyLzcgIyBub3QgcXVpdGUgcGkKCiMgSWYgd2UgbmVlZCBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIHBpLCB3ZSBjYW4gdXNlIEV1bGVyJ3MgZm9ybXVsYQojIFRoaXMgdXNlcyBhdGFuKCksIHdoaWNoIGNhbGN1bGF0ZXMgYXJjdGFuZ2VudC4KMjAgKiBhdGFuKDEvNykgKyA4ICogYXRhbigzLzc5KSAKYGBgCgpIZWxwIG91dCBGdXR1cmUgWW91IGJ5IGFkZGluZyBsb3RzIG9mIGNvbW1lbnRzISAKRnV0dXJlIFlvdSBuZXh0IHdlZWsgdGhpbmtzIFRvZGF5IFlvdSBpcyBhbiBpZGlvdCwgYW5kIHRoZSBvbmx5IHdheSB5b3UgY2FuIGNvbnZpbmNlIEZ1dHVyZSBZb3UgdGhhdCBUb2RheSBZb3UgaXMgcmVhc29uYWJseSBjb21wZXRlbnQgaXMgYnkgYWRkaW5nIGNvbW1lbnRzIGluIHlvdXIgY29kZSBleHBsYWluaW5nIHdoeSBUb2RheSBZb3UgaXMgYWN0dWFsbHkgbm90IHNvIGJhZC4KCiMjIEZ1bmN0aW9ucwpXZSBjYW4gdXNlIHByZS1idWlsdCBjb21wdXRhdGlvbiBtZXRob2RzIGNhbGxlZCAiZnVuY3Rpb25zIiBmb3Igb3RoZXIgb3BlcmF0aW9ucy4gCkZ1bmN0aW9ucyBoYXZlIHRoZSBmb2xsb3dpbmcgZm9ybWF0LCB3aGVyZSB0aGUgX2FyZ3VtZW50XyBpcyB0aGUgaW5mb3JtYXRpb24gd2UgYXJlIHByb3ZpZGluZyB0byB0aGUgZnVuY3Rpb24gZm9yIGl0IHRvIHJ1bi4gCkFuIGV4YW1wbGUgb2YgdGhpcyB3YXMgdGhlIGBhdGFuKClgIGZ1bmN0aW9uIHVzZWQgYWJvdmUuCgpgYGByCmZ1bmN0aW9uX25hbWUoYXJndW1lbnQpCmBgYAoKVG8gbGVhcm4gYWJvdXQgZnVuY3Rpb25zLCB3ZSdsbCBleGFtaW5lIG9uZSBjYWxsZWQgYGxvZygpYCBmaXJzdC4gCgpUbyBrbm93IHdoYXQgYSBmdW5jdGlvbiBkb2VzIGFuZCBob3cgdG8gdXNlIGl0LCB1c2UgdGhlIHF1ZXN0aW9uIG1hcmsgd2hpY2ggd2lsbCByZXZlYWwgZG9jdW1lbnRhdGlvbiBpbiB0aGUgKipoZWxwIHBhbmUqKjogYD9sb2dgCiFbcmhlbHBdKHNjcmVlbnNob3RzL3JoZWxwLWxvZy5wbmcpIAoKVGhlIGRvY3VtZW50YXRpb24gdGVsbHMgdXMgdGhhdCBgbG9nKClgIGlzIGRlcml2ZWQgZnJvbSBge2Jhc2V9YCwgbWVhbmluZyBpdCBpcyBhIGZ1bmN0aW9uIHRoYXQgaXMgcGFydCBvZiBiYXNlIFIuIApJdCBwcm92aWRlcyBhIGJyaWVmIGRlc2NyaXB0aW9uIG9mIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMgYW5kIHNob3dzIHNldmVyYWwgZXhhbXBsZXMgb2YgdG8gaG93IHVzZSBpdC4KCkluIHBhcnRpY3VsYXIsIHRoZSBkb2N1bWVudGF0aW9uIHRlbGxzIHVzIGFib3V0IHdoYXQgYXJndW1lbnQocykgdG8gcHJvdmlkZToKCisgVGhlIGZpcnN0IF9yZXF1aXJlZF8gYXJndW1lbnQgaXMgdGhlIHZhbHVlIHdlJ2QgbGlrZSB0byB0YWtlIHRoZSBsb2cgb2YsIGJ5IGRlZmF1bHQgaXRzIF9uYXR1cmFsIGxvZ18KKyBUaGUgc2Vjb25kIF9vcHRpb25hbF8gYXJndW1lbnQgY2FuIHNwZWNpZnkgYSBkaWZmZXJlbnQgYmFzZSByYXRoZXIgdGhhbiB0aGUgZGVmYXVsdCBgZWAuCgpGdW5jdGlvbnMgYWxzbyBfcmV0dXJuXyB2YWx1ZXMgZm9yIHVzIHRvIHVzZS4gCkluIHRoZSBjYXNlIG9mIGBsb2coKWAsIHRoZSByZXR1cm5lZCB2YWx1ZSBpcyB0aGUgbG9nJ2QgdmFsdWUgdGhlIGZ1bmN0aW9uIGNvbXB1dGVkLgoKYGBge3IgbG9nfQpsb2coNzMpCmBgYAoKSGVyZSB3ZSBjYW4gc3BlY2lmeSBhbiBfYXJndW1lbnRfIG9mIGBiYXNlYCB0byBjYWxjdWxhdGUgbG9nIGJhc2UgMy4gCgpgYGB7ciBsb2czfQpsb2coODEsIGJhc2UgPSAzKQpgYGAKCklmIHdlIGRvbid0IHNwZWNpZnkgdGhlIF9hcmd1bWVudF8gbmFtZXMsIGl0IGFzc3VtZXMgdGhleSBhcmUgaW4gdGhlIG9yZGVyIHRoYXQgYGxvZ2AgZGVmaW5lcyB0aGVtLiAKU2VlIGA/bG9nYCB0byBzZWUgbW9yZSBhYm91dCBpdHMgYXJndW1lbnRzLiAKCmBgYHtyIGxvZzIsIGxpdmUgPSBUUlVFfQpsb2coOCwgMikKYGBgCgpXZSBjYW4gc3dpdGNoIHRoZSBvcmRlciBpZiB3ZSBzcGVjaWZ5IHRoZSBhcmd1bWVudCBuYW1lcy4gCgpgYGB7ciBsb2ctb3JkZXJ9CmxvZyhiYXNlID0gMTAsIHggPSA0MzQyKQpgYGAKCldlIGNhbiBhbHNvIHByb3ZpZGUgdmFyaWFibGVzIGFzIGFyZ3VtZW50cyBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIHJhdyB2YWx1ZXMuIAoKYGBge3IgbG9nLXZhcmlhYmxlfQptZWFuaW5nIDwtIDQyCmxvZyhtZWFuaW5nKQpgYGAKCiMjIFdvcmtpbmcgd2l0aCB2YXJpYWJsZXMKCiMjIyBWYXJpYWJsZSBUeXBlcwoKVmFyaWFibGUgdHlwZXMgaW4gUiBjYW4gc29tZXRpbWVzIGJlIF9jb2VyY2VkXyAoY29udmVydGVkKSBmcm9tIG9uZSB0eXBlIHRvIGFub3RoZXIuCgpgYGB7cn0KIyBEZWZpbmUgYSB2YXJpYWJsZSB3aXRoIGEgbnVtYmVyCnggPC0gMTUKYGBgCgpUaGUgZnVuY3Rpb24gYGNsYXNzKClgIHdpbGwgdGVsbCB1cyB0aGUgdmFyaWFibGUncyB0eXBlLgoKYGBge3J9CmNsYXNzKHgpCmBgYAoKTGV0J3MgY29lcmNlIGl0IHRvIGEgY2hhcmFjdGVyLiAKCmBgYHtyfQp4IDwtIGFzLmNoYXJhY3Rlcih4KQpjbGFzcyh4KQpgYGAKClNlZSBpdCBub3cgaGFzIHF1b3RlcyBhcm91bmQgaXQ/IEl0J3Mgbm93IGEgY2hhcmFjdGVyIGFuZCB3aWxsIGJlaGF2ZSBhcyBzdWNoLgoKYGBge3J9CngKYGBgCgpVc2UgdGhpcyBjaHVuayB0byB0cnkgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgd2l0aCBgeGAsIG5vdyB0aGF0IGl0IGlzIGEgY2hhcmFjdGVyLCB3aGF0IGhhcHBlbnM/IAoKYGBge3IgbGl2ZSA9IFRSVUV9CiMgVHJ5IHRvIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGB4YApgYGAKCkJ1dCB3ZSBjYW4ndCBjb2VyY2UgZXZlcnl0aGluZzoKCmBgYHtyfQojIExldCdzIGNyZWF0ZSBhIGNoYXJhY3RlciB2YXJpYWJsZQp4IDwtICJsb29rIGF0IG15IGNoYXJhY3RlciB2YXJpYWJsZSIKYGBgCgpMZXQncyB0cnkgbWFraW5nIHRoaXMgYSBudW1lcmljIHZhcmlhYmxlOgoKYGBge3IgY29lcmNlLWNoYXIsIGVycm9yPVRSVUV9CnggPC0gYXMubnVtZXJpYyh4KQpgYGAKClByaW50IG91dCBgeGAuCgpgYGB7cn0KeApgYGAKClIgaXMgdGVsbGluZyB1cyBpdCBkb2Vzbid0IGtub3cgaG93IHRvIGNvbnZlcnQgdGhpcyB0byBhIG51bWVyaWMgdmFyaWFibGUsIHNvIGl0IGhhcyByZXR1cm5lZCBgTkFgIGluc3RlYWQuCgpGb3IgcmVmZXJlbmNlLCBoZXJlJ3MgYSBzdW1tYXJ5IG9mIHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlIHR5cGVzLiAKCnwgVmFyaWFibGUgVHlwZSB8IERlZmluaXRpb24gfCBFeGFtcGxlcyB8IENvZXJjaW9uIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwgLS0tLS0tLS18CnwgYG51bWVyaWNgICAgICAgIHwgQW55IG51bWJlciB2YWx1ZSB8IGA1YDxicj5gNy41YCA8YnI+YC0xYHwgYGFzLm51bWVyaWMoKWAKfCBgaW50ZWdlcmAgICAgICAgfCBBbnkgX3dob2xlXyBudW1iZXIgdmFsdWUgKG5vIGRlY2ltYWxzKSB8IGA1YCA8YnI+IGAtMTAwYCB8IGBhcy5pbnRlZ2VyKClgCnxgY2hhcmFjdGVyYCAgICAgIHwgQW55IGNvbGxlY3Rpb24gb2YgY2hhcmFjdGVycyBkZWZpbmVkIHdpdGhpbiBfcXVvdGF0aW9uIG1hcmtzXy4gQWxzbyBrbm93biBhcyBhICJzdHJpbmciLiB8IGAiYSJgIChhIHNpbmdsZSBsZXR0ZXIpIDxicj5gInN0cmluZ29mbGV0dGVycyJgIChhIHdob2xlIGJ1bmNoIG9mIGNoYXJhY3RlcnMgcHV0IHRvZ2V0aGVyIGFzIG9uZSkgPGJyPiBgInN0cmluZyBvZiBsZXR0ZXJzIGFuZCBzcGFjZXMiYCA8YnI+IGAiNSJgIDxicj4gYCdzaW5nbGUgcXVvdGVzIGFyZSBhbHNvIGdvb2QnYCB8IGBhcy5jaGFyYWN0ZXIoKWAKfGBsb2dpY2FsYCAgICAgIHwgQSB2YWx1ZSBvZiBgVFJVRWAsIGBGQUxTRWAsIG9yIGBOQWAgfCBgVFJVRWAgPGJyPiBgRkFMU0VgIDxicj4gYE5BYCAobm90IGRlZmluZWQpIHwgYGFzLmxvZ2ljYWwoKWAgCnxgZmFjdG9yYCAgICAgICB8IEEgc3BlY2lhbCB0eXBlIG9mIHZhcmlhYmxlIHRoYXQgZGVub3RlcyBzcGVjaWZpYyBjYXRlZ29yaWVzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgfCAoc3RheSB0dW5lZC4uKSB8IGBhcy5mYWN0b3IoKWAKCiMjIyBWZWN0b3JzCgpZb3Ugd2lsbCBoYXZlIG5vdGljZWQgdGhhdCBhbGwgeW91ciBjb21wdXRhdGlvbnMgdGVuZCB0byBwb3AgdXAgd2l0aCBhIGBbMV1gIHByZWNlZGluZyB0aGVtIGluIFIncyBvdXRwdXQuIApUaGlzIGlzIGJlY2F1c2UsIGluIGZhY3QsIGFsbCAob2sgbW9zdGx5IGFsbCkgdmFyaWFibGVzIGFyZSBfYnkgZGVmYXVsdF8gIHZlY3RvcnMsIGFuZCBvdXIgYW5zd2VycyBhcmUgdGhlIGZpcnN0IChpbiB0aGVzZSBjYXNlcyBvbmx5KSB2YWx1ZSBpbiB0aGUgdmVjdG9yLiAKQXMgdmVjdG9ycyBnZXQgbG9uZ2VyLCBuZXcgaW5kZXggaW5kaWNhdG9ycyB3aWxsIGFwcGVhciBhdCB0aGUgc3RhcnQgb2YgbmV3IGxpbmVzLiAKCmBgYHtyfQojIFRoaXMgaXMgYWN0dWFsbHkgYW4gdmVjdG9yIHRoYXQgaGFzIG9uZSBpdGVtIGluIGl0Lgp4IDwtIDcKYGBgCgpgYGB7ciB2ZWN0b3ItbGVuZ3RofQojIFRoZSBsZW5ndGgoKSBmdW5jdGlvbnMgdGVsbHMgdXMgaG93IGxvbmcgYW4gdmVjdG9yIGlzOgpsZW5ndGgoeCkKYGBgCgpXZSBjYW4gZGVmaW5lIHZlY3RvcnMgd2l0aCB0aGUgZnVuY3Rpb24gYGMoKWAsIHdoaWNoIHN0YW5kcyBmb3IgImNvbWJpbmUiLiAKVGhpcyBmdW5jdGlvbiB0YWtlcyBhIGNvbW1hLXNlcGFyYXRlZCBzZXQgb2YgdmFsdWVzIHRvIHBsYWNlIGluIHRoZSB2ZWN0b3IsIGFuZCByZXR1cm5zIHRoZSB2ZWN0b3IgaXRzZWxmOgoKYGBge3IgbWFrZS12ZWN0b3J9Cm15X251bWVyaWNfdmVjdG9yIDwtIGMoMSwgMSwgMiwgMywgNSwgOCwgMTMsIDIxKQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBidWlsZCBvbiB2ZWN0b3JzIGluIHBsYWNlIGJ5IHJlZGVmaW5pbmcgdGhlbToKCmBgYHtyIGZpYmJvbmFjY2ksIGxpdmUgPSBUUlVFfQojIGFkZCB0aGUgbmV4dCB0d28gRmlib25hY2NpIG51bWJlcnMgdG8gdGhlIHNlcmllcy4KbXlfbnVtZXJpY192ZWN0b3IgPC0gYyhteV9udW1lcmljX3ZlY3RvciwgMzQsIDU1KQpteV9udW1lcmljX3ZlY3RvcgpgYGAKCldlIGNhbiBwdWxsIG91dCBzcGVjaWZpYyBpdGVtcyBmcm9tIGFuIHZlY3RvciB1c2luZyBhIHByb2Nlc3MgY2FsbGVkIF9pbmRleGluZ18sIHdoaWNoIHVzZXMgYnJhY2tldHMgYFtdYCB0byBzcGVjaWZ5IHRoZSBwb3NpdGlvbiBvZiBhbiBpdGVtLiAKCmBgYHtyIHN1YnNldDF9CiMgR3JhYiB0aGUgZm91cnRoIHZhbHVlIGZyb20gbXlfbnVtZXJpY192ZWN0b3IKIyBUaGlzIGdpdmVzIHVzIGFuIHZlY3RvciBvZiBsZW5ndGggMSAKbXlfbnVtZXJpY192ZWN0b3JbNF0KYGBgCgpDb2xvbnMgYXJlIGFsc28gYSBuaWNlIHdheSB0byBxdWlja2x5IG1ha2Ugb3JkZXJlZCBudW1lcmljIHZlY3RvcnMKVXNlIGEgY29sb24gdG8gc3BlY2lmeSBhbiBpbmNsdXNpdmUgcmFuZ2Ugb2YgaW5kaWNlcwpUaGlzIHdpbGwgcmV0dXJuIGFuIHZlY3RvciB3aXRoIDIsIDMsIDQsIGFuZCA1LgoKYGBge3Igc3Vic2V0LW1hbnl9Cm15X251bWVyaWNfdmVjdG9yWzI6NV0KYGBgCgpPbmUgbWFqb3IgYmVuZWZpdCBvZiB2ZWN0b3JzIGlzIHRoZSBjb25jZXB0IG9mICoqdmVjdG9yaXphdGlvbioqLCB3aGVyZSBSIGJ5IGRlZmF1bHQgcGVyZm9ybXMgb3BlcmF0aW9ucyBvbiB0aGUgX2VudGlyZSB2ZWN0b3IgYXQgb25jZV8uIApGb3IgZXhhbXBsZSwgd2UgY2FuIGdldCB0aGUgbG9nIG9mIGFsbCBudW1iZXJzIDEtMjAgd2l0aCBhIHNpbmdsZSwgc2ltcGxlIGNhbGwsIGFuZCBtb3JlIQoKYGBge3IgdmVjdG9yaXplfQp2YWx1ZXNfMV90b18yMCA8LSAxOjIwCmBgYAoKCmBgYHtyIHZlY3Rvcml6ZS1sb2csIGxpdmUgPSBUUlVFfQojIGNhbGN1bGF0ZSB0aGUgbG9nIG9mIHZhbHVlc18xX3RvXzIwCmxvZyh2YWx1ZXNfMV90b18yMCkKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gYXBwbHkgbG9naWNhbCBleHByZXNzaW9ucyB0byB2ZWN0b3JzLCBqdXN0IGFzIHdlIGNhbiBkbyBmb3Igc2luZ2xlIHZhbHVlcy4KVGhlIG91dHB1dCBoZXJlIGlzIGEgbG9naWNhbCB2ZWN0b3IgdGVsbGluZyB1cyB3aGV0aGVyIGVhY2ggdmFsdWUgaW4gZXhhbXBsZV92ZWN0b3IgaXMgVFJVRSBvciBGQUxTRQoKYGBge3IgdmVjdG9yLWNvbXBhcmV9CiMgV2hpY2ggdmFsdWVzIGFyZSA8PSAzPwp2YWx1ZXNfMV90b18yMCA8PSAzCmBgYAoKVGhlcmUgYXJlIHNldmVyYWwga2V5IGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgdXNlZCBvbiB2ZWN0b3JzIGNvbnRhaW5pbmcgbnVtZXJpYyB2YWx1ZXMsIHNvbWUgb2Ygd2hpY2ggYXJlIGJlbG93LgoKKyBgbWVhbigpYDogVGhlIGF2ZXJhZ2UgdmFsdWUgaW4gdGhlIHZlY3RvcgorIGBtaW4oKWA6IFRoZSBtaW5pbXVtIHZhbHVlIGluIHRoZSB2ZWN0b3IKKyBgbWF4KClgOiBUaGUgbWF4aW11bSB2YWx1ZSBpbiB0aGUgdmVjdG9yCisgYHN1bSgpYDogVGhlIHN1bSBvZiBhbGwgdmFsdWVzIGluIHRoZSB2ZWN0b3IKCldlIGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyBvbiB0aGUgdmVjdG9yIGB2YWx1ZXNfMV90b18yMGAgd2UndmUgY3JlYXRlZC4gCgpgYGB7ciB2ZWN0b3ItZnVuY3N9Cm1lYW4odmFsdWVzXzFfdG9fMjApCgojIFRyeSBvdXQgc29tZSBvZiB0aGUgb3RoZXIgZnVuY3Rpb25zIHdlJ3ZlIGxpc3RlZCBhYm92ZSAKCmBgYAoKIyMjIEEgbm90ZSBvbiB2YXJpYWJsZSBuYW1pbmcKCldlIGhhdmUgbGVhcm5lZCBmdW5jdGlvbnMgc3VjaCBhcyBgY2AsIGBsZW5ndGhgLCBgc3VtYCwgYW5kIGV0Yy4gCkltYWdpbmUgZGVmaW5pbmcgYSB2YXJpYWJsZSBjYWxsZWQgYGNgOiBUaGlzIHdpbGwgd29yaywgYnV0IGl0IHdpbGwgbGVhZCB0byBhIApsb3Qgb2YgdW5pbnRlbmRlZCBidWdzLCBzbyBpdCdzIGJlc3QgdG8gYXZvaWQgdGhpcy4gCgojIyMgVGhlIGAlaW4lYCBsb2dpY2FsIG9wZXJhdG9yIAoKYCVpbiVgIGlzIHVzZWZ1bCBmb3IgZGV0ZXJtaW5pbmcgd2hldGhlciBhIGdpdmVuIGl0ZW0ocykgYXJlIGluIGFuIHZlY3Rvci4KCmBgYHtyIGluLW9wZXJhdG9yfQojIGlzIGA3YCBpbiBvdXIgdmVjdG9yPyAKNyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKYGBge3IgaW4yLCBsaXZlID0gVFJVRX0KIyBpcyBgNTBgIGluIG91ciB2ZWN0b3I/IAo1MCAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKV2UgY2FuIHRlc3QgYSB2ZWN0b3Igb2YgdmFsdWVzIGJlaW5nIHdpdGhpbiBhbm90aGVyIHZlY3RvciBvZiB2YWx1ZXMuIAoKYGBge3IgdmVjdG9yLWluLCBsaXZlID0gVFJVRX0KcXVlc3Rpb25fdmFsdWVzIDwtIGMoMTozLCA3LCA1MCkKIyBBcmUgdGhlc2UgdmFsdWVzIGluIG91ciB2ZWN0b3I/CnF1ZXN0aW9uX3ZhbHVlcyAlaW4lIHZhbHVlc18xX3RvXzIwCmBgYAoKIyMgRGF0YSBmcmFtZXMKCl9EYXRhIGZyYW1lcyBhcmUgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBmb3IgZGF0YSBhbmFseXNpcyBpbiBSLl8gClRoZXkgYXJlIHRhYmxlcyB3aGljaCBjb25zaXN0IG9mIHJvd3MgYW5kIGNvbHVtbnMsIG11Y2ggbGlrZSBhIF9zcHJlYWRzaGVldF8uIApFYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlIHdoaWNoIGJlaGF2ZXMgYXMgYSBfdmVjdG9yXywgYW5kIGVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uLiAKV2Ugd2lsbCBiZWdpbiBvdXIgZXhwbG9yYXRpb24gd2l0aCBkYXRhc2V0IG9mIG1lYXN1cmVtZW50cyBmcm9tIHRocmVlIHBlbmd1aW4gc3BlY2llcyBtZWFzdXJlZCwgd2hpY2ggd2UgY2FuIGZpbmQgaW4gdGhlIFtgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2VdKGh0dHBzOi8vYWxsaXNvbmhvcnN0LmdpdGh1Yi5pby9wYWxtZXJwZW5ndWlucy8pLiAKV2UnbGwgdGFsayBtb3JlIGFib3V0IHBhY2thZ2VzIHNvb24hClRvIHVzZSB0aGlzIGRhdGFzZXQsIHdlIHdpbGwgbG9hZCBpdCBmcm9tIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UgdXNpbmcgYSBgOjpgIChtb3JlIG9uIHRoaXMgbGF0ZXIpIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lZCBgcGVuZ3VpbnNgIGluIG91ciBjdXJyZW50IGVudmlyb25tZW50LgoKYGBge3IgcGVuZ3Vpbi1saWJyYXJ5fQpwZW5ndWlucyA8LSBwYWxtZXJwZW5ndWluczo6cGVuZ3VpbnMKYGBgCgohW2RyYXdpbmdzIG9mIHBlbmd1aW4gc3BlY2llc10oZGlhZ3JhbXMvbHRlcl9wZW5ndWlucy5wbmcpIEFydHdvcmsgYnkgW0BhbGxpc29uX2hvcnN0XShodHRwczovL3R3aXR0ZXIuY29tL2FsbGlzb25faG9yc3QpCgojIyMgRXhwbG9yaW5nIGRhdGEgZnJhbWVzCgpUaGUgZmlyc3Qgc3RlcCB0byB1c2luZyBhbnkgZGF0YSBpcyB0byBsb29rIGF0IGl0ISEhIApSU3R1ZGlvIGNvbnRhaW5zIGEgc3BlY2lhbCBmdW5jdGlvbiBgVmlldygpYCB3aGljaCBhbGxvd3MgeW91IHRvIGxpdGVyYWxseSB2aWV3IGEgdmFyaWFibGUuCllvdSBjYW4gYWxzbyBjbGljayBvbiB0aGUgb2JqZWN0IGluIHRoZSBlbnZpcm9ubWVudCBwYW5lIHRvIHNlZSBpdHMgb3ZlcmFsbCBwcm9wZXJ0aWVzLCBvciBjbGljayB0aGUgdGFibGUgaWNvbiBvbiB0aGUgb2JqZWN0J3Mgcm93IHRvIGF1dG9tYXRpY2FsbHkgdmlldyB0aGUgdmFyaWFibGUuIAoKU29tZSB1c2VmdWwgZnVuY3Rpb25zIGZvciBleHBsb3Jpbmcgb3VyIGRhdGEgZnJhbWUgaW5jbHVkZToKCisgYGhlYWQoKWAgdG8gc2VlIHRoZSBmaXJzdCA2IHJvd3Mgb2YgYSBkYXRhIGZyYW1lLiBBZGRpdGlvbmFsIGFyZ3VtZW50cyBzdXBwbGllZCBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2Ygcm93cy4KKyBgdGFpbCgpYCB0byBzZWUgdGhlIGxhc3QgNiByb3dzIG9mIGEgZGF0YSBmcmFtZS4gQWRkaXRpb25hbCBhcmd1bWVudHMgc3VwcGxpZWQgY2FuIGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MuCisgYG5hbWVzKClgIHRvIHNlZSB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBkYXRhIGZyYW1lLgorIGBucm93KClgIHRvIHNlZSBob3cgbWFueSByb3dzIGFyZSBpbiB0aGUgZGF0YSBmcmFtZQorIGBuY29sKClgIHRvIHNlZSBob3cgbWFueSBjb2x1bW5zIGFyZSBpbiB0aGUgZGF0YSBmcmFtZS4KCldlIGNhbiBhZGRpdGlvbmFsbHkgZXhwbG9yZSBfb3ZlcmFsbCBwcm9wZXJ0aWVzXyBvZiB0aGUgZGF0YSBmcmFtZSB3aXRoIHR3byBkaWZmZXJlbnQgZnVuY3Rpb25zOiBgc3VtbWFyeSgpYCBhbmQgYHN0cigpYC4KClRoaXMgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGNvbHVtbjoKCmBgYHtyIHBlbmd1aW5zLXN1bW1hcnl9CnN1bW1hcnkocGVuZ3VpbnMpCmBgYAoKVGhpcyBwcm92aWRlcyBhIHNob3J0IHZpZXcgb2YgdGhlICoqc3RyKip1Y3R1cmUgYW5kIGNvbnRlbnRzIG9mIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgcGVuZ3VpbnMtc3RyfQpzdHIocGVuZ3VpbnMpCmBgYAoKWW91J2xsIG5vdGljZSB0aGF0IHRoZSBjb2x1bW4gYHNwZWNpZXNgIGlzIGEgX2ZhY3Rvcl86IFRoaXMgaXMgYSBzcGVjaWFsIHR5cGUgb2YgY2hhcmFjdGVyIHZhcmlhYmxlIHRoYXQgcmVwcmVzZW50cyBkaXN0aW5jdCBjYXRlZ29yaWVzIGtub3duIGFzICJsZXZlbHMiLiAKV2UgaGF2ZSBsZWFybmVkIGhlcmUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgbGV2ZWxzIGluIHRoZSBgc3BlY2llc2AgY29sdW1uOiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvby4KV2UgbWlnaHQgd2FudCB0byBleHBsb3JlIGluZGl2aWR1YWwgY29sdW1ucyBvZiB0aGUgZGF0YSBmcmFtZSBtb3JlIGluLWRlcHRoLiAKV2UgY2FuIGV4YW1pbmUgaW5kaXZpZHVhbCBjb2x1bW5zIHVzaW5nIHRoZSBkb2xsYXIgc2lnbiBgJGAgdG8gc2VsZWN0IG9uZSBieSBuYW1lOgoKYGBge3IgcGVuZ3VpbnMtc3Vic2V0fQojIEV4dHJhY3QgYmlsbF9sZW5ndGhfbW0gYXMgYSB2ZWN0b3IKcGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0KCiMgaW5kZXhpbmcgb3BlcmF0b3JzIGNhbiBiZSB1c2VkIG9uIHRoZXNlIHZlY3RvcnMgdG9vCnBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tWzE6MTBdCmBgYAoKV2UgY2FuIHBlcmZvcm0gb3VyIHJlZ3VsYXIgdmVjdG9yIG9wZXJhdGlvbnMgb24gY29sdW1ucyBkaXJlY3RseS4KCmBgYHtyIHBlbmd1aW5zLWNvbC1tZWFuLCBsaXZlID0gVFJVRX0KIyBjYWxjdWxhdGUgdGhlIG1lYW4gb2YgdGhlIGJpbGxfbGVuZ3RoX21tIGNvbHVtbgptZWFuKHBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tLAogICAgIG5hLnJtID0gVFJVRSkgIyByZW1vdmUgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBtZWFuCmBgYAoKV2UgY2FuIGFsc28gY2FsY3VsYXRlIHRoZSBmdWxsIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBzaW5nbGUgY29sdW1uIGRpcmVjdGx5LiAKCmBgYHtyIHBlbmd1aW5zLWNvbC1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzaG93IGEgc3VtbWFyeSBvZiB0aGUgYmlsbF9sZW5ndGhfbW0gY29sdW1uCnN1bW1hcnkocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0pCmBgYAoKRXh0cmFjdCBgc3BlY2llc2AgYXMgYSB2ZWN0b3IgYW5kIHN1YnNldCBpdCB0byBzZWUgYSBwcmV2aWV3LgoKYGBge3IgcGVuZ3VpbnMtY29sLXN1YnNldCwgbGl2ZSA9IFRSVUV9CiMgZ2V0IHRoZSBmaXJzdCAxMCB2YWx1ZXMgb2YgdGhlIHNwZWNpZXMgY29sdW1uCnBlbmd1aW5zJHNwZWNpZXNbMToxMF0KYGBgCgpBbmQgdmlldyBpdHMgX2xldmVsc18gd2l0aCB0aGUgYGxldmVscygpYCBmdW5jdGlvbi4KCmBgYHtyIHBlbmd1aW4tbGV2ZWxzfQpsZXZlbHMocGVuZ3VpbnMkc3BlY2llcykKYGBgCgojIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCkluIG1hbnkgc2l0dWF0aW9ucywgd2Ugd2lsbCBiZSByZWFkaW5nIGluIHRhYnVsYXIgZGF0YSBmcm9tIGEgZmlsZSBhbmQgdXNpbmcgaXQgYXMgYSBkYXRhIGZyYW1lLiAKVG8gcHJhY3RpY2UsIHdlIHdpbGwgcmVhZCBpbiBhIGZpbGUgd2Ugd2lsbCBiZSB1c2luZyBpbiB0aGUgbmV4dCBub3RlYm9vayBhcyB3ZWxsLCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAsIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gdGhlIGxvY2F0aW9uIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuCgpIZXJlIHdlIHdpbGwgdXNlIGEgZnVuY3Rpb24sIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkJlZm9yZSB3ZSBhcmUgYWJsZSB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBoYXZlIHRvIGxvYWQgdGhlIHBhY2thZ2UgdXNpbmcgYGxpYnJhcnkoKWAuIAoKYGBge3IgcmVhZHJ9CmxpYnJhcnkocmVhZHIpCmBgYAoKYGZpbGUucGF0aCgpYCBjcmVhdGVzIGEgcHJvcGVybHkgZm9ybWF0dGVkIGZpbGUgcGF0aCBieSBhZGRpbmcgYSBwYXRoIHNlcGFyYXRvciAoYC9gIG9uIE1hYyBhbmQgTGludXggb3BlcmF0aW5nIHN5c3RlbXMsIHRoZSBsYXR0ZXIgb2Ygd2hpY2ggaXMgdGhlIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCBvdXIgUlN0dWRpbyBTZXJ2ZXIgcnVucyBvbikgYmV0d2VlbiBzZXBhcmF0ZSBmb2xkZXJzIG9yIGRpcmVjdG9yaWVzLgpCZWNhdXNlIGZpbGUgcGF0aCBzZXBhcmF0b3JzIGNhbiBkaWZmZXIgYmV0d2VlbiB5b3VyIGNvbXB1dGVyIGFuZCB0aGUgY29tcHV0ZXIgb2Ygc29tZW9uZSB3aG8gd2FudHMgdG8gdXNlIHlvdXIgY29kZSwgd2UgdXNlIGBmaWxlLnBhdGgoKWAgaW5zdGVhZCBvZiB0eXBpbmcgb3V0IGAiZGF0YS9nZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2ImAuCkVhY2ggX2FyZ3VtZW50XyB0byBgZmlsZS5wYXRoKClgIGlzIGEgZGlyZWN0b3J5IG9yIGZpbGUgbmFtZS4KWW91J2xsIG5vdGljZSBlYWNoIGFyZ3VtZW50IGlzIGluIHF1b3Rlcywgd2Ugc3BlY2lmeSBgZGF0YWAgZmlyc3QgYmVjYXVzZSB0aGUgZmlsZSwgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGlzIGluIHRoZSBgZGF0YWAgZm9sZGVyLiAKCmBgYHtyIGZpbGUucGF0aH0KZmlsZS5wYXRoKCJkYXRhIiwgImdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3YiKQpgYGAKCkFzIHlvdSBjYW4gc2VlIGFib3ZlLCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgYGZpbGUucGF0aCgpYCBpcyB0aGF0IGl0IF9jcmVhdGVzIGEgc3RyaW5nXyB3aXRoIGFuIGFjY3VyYXRlbHktZm9ybWF0dGVkIHBhdGggZm9yIHlvdXIgZmlsZSBzeXN0ZW0uClRoaXMgc3RyaW5nIGNhbiBiZSB1c2VkIG1vdmluZyBmb3J3YXJkIHdoZW4geW91IG5lZWQgdG8gcmVmZXIgdG8gdGhlIHBhdGggdG8geW91ciBmaWxlLgpMZXQncyBnbyBhaGVhZCBhbmQgc3RvcmUgdGhpcyBmaWxlIHBhdGggYXMgYSB2YXJpYWJsZSBpbiBvdXIgZW52aXJvbm1lbnQuIAoKYGBge3IgZmlsZS5wYXRoLXZhcmlhYmxlfQpnZW5lX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIpCmBgYAoKTm93IHdlIGFyZSByZWFkeSB0byB1c2UgYHJlYWRfdHN2KClgIHRvIHJlYWQgdGhlIGZpbGUgaW50byBSLgpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2lsbCBiZSBzdG9yZWQgaW4gYSB2YXJpYWJsZSBuYW1lZCBgc3RhdHNfZGZgLgpOb3RlIHRoZSBgPC1gIChhc3NpZ25tZW50IG9wZXJhdG9yISkgaXMgcmVzcG9uc2libGUgZm9yIHNhdmluZyB0aGlzIHRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuIAoKYGBge3IgcmVhZC1zdGF0c30KIyByZWFkIGluIHRoZSBmaWxlIGBnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2YCBmcm9tIHRoZSBkYXRhIGRpcmVjdG9yeQpzdGF0c19kZiA8LSByZWFkX3RzdihnZW5lX2ZpbGVfcGF0aCkKYGBgCgpUYWtlIGEgbG9vayBhdCB5b3VyIGVudmlyb25tZW50IHBhbmVsIHRvIHNlZSB3aGF0IGBzdGF0c19kZmAgbG9va3MgbGlrZS4gCldlIGNhbiBhbHNvIHByaW50IG91dCBhIHByZXZpZXcgb2YgdGhlIGBzdGF0c19kZmAgZGF0YSBmcmFtZSBoZXJlLiAKCmBgYHtyIHNob3ctc3RhdHMsIGxpdmUgPSBUUlVFfQojIGRpc3BsYXkgc3RhdHNfZGYKc3RhdHNfZGYKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpBdCB0aGUgZW5kIG9mIGV2ZXJ5IG5vdGVib29rLCB5b3Ugd2lsbCBzZWUgdXMgcHJpbnQgb3V0IGBzZXNzaW9uSW5mb2AuIApUaGlzIGFpZHMgaW4gdGhlIHJlcHJvZHVjaWJpbGl0eSBvZiB5b3VyIGNvZGUgYnkgc2hvd2luZyBleGFjdGx5IHdoYXQgcGFja2FnZXMgCmFuZCB2ZXJzaW9ucyB3ZXJlIGJlaW5nIHVzZWQgdGhlIGxhc3QgdGltZSB0aGUgbm90ZWJvb2sgd2FzIHJ1bi4KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=
diff --git a/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd b/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd
index 2e854b26..5033057d 100644
--- a/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd
+++ b/intro-to-R-tidyverse/02-intro_to_ggplot2-live.Rmd
@@ -34,7 +34,7 @@ We performed three sets of contrasts:
- [ggplot2 website](https://ggplot2.tidyverse.org/)
- [Handy cheatsheet for ggplot2 (pdf)](https://github.com/rstudio/cheatsheets/raw/main/data-visualization.pdf)
- [_Data Visualization, A practical introduction_](https://socviz.co/)
-- [Data visualization chapter of _R for Data Science_](https://r4ds.had.co.nz/data-visualisation.html)
+- [Data visualization chapter of _R for Data Science_](https://r4ds.hadley.nz/data-visualize.html)
- [ggplot2 online tutorial](http://r-statistics.co/Complete-Ggplot2-Tutorial-Part1-With-R-Code.html)
## Set Up
diff --git a/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html b/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html
index 929e4c1a..f60de6a1 100644
--- a/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html
+++ b/intro-to-R-tidyverse/02-intro_to_ggplot2.nb.html
@@ -2917,7 +2917,7 @@ Objectives
cheatsheet for ggplot2 (pdf)
Data Visualization, A practical
introduction
-Data
+Data
visualization chapter of R for Data Science
ggplot2
online tutorial
@@ -3521,8 +3521,8 @@ Session Info
# Print out the versions and packages we are using in this session
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3552,23 +3552,23 @@ Session Info
loaded via a namespace (and not attached):
[1] sass_0.4.9 utf8_1.2.4 generics_0.1.3 stringi_1.8.3
[5] hms_1.1.3 digest_0.6.35 magrittr_2.0.3 evaluate_0.23
- [9] grid_4.4.0 timechange_0.3.0 fastmap_1.1.1 jsonlite_1.8.8
+ [9] grid_4.4.1 timechange_0.3.0 fastmap_1.1.1 jsonlite_1.8.8
[13] fansi_1.0.6 scales_1.3.0 textshaping_0.3.7 getopt_1.20.4
[17] jquerylib_0.1.4 cli_3.6.2 crayon_1.5.2 rlang_1.1.3
[21] bit64_4.0.5 munsell_0.5.1 withr_3.0.0 cachem_1.0.8
-[25] yaml_2.3.8 parallel_4.4.0 tools_4.4.0 tzdb_0.4.0
+[25] yaml_2.3.8 parallel_4.4.1 tools_4.4.1 tzdb_0.4.0
[29] colorspace_2.1-0 vctrs_0.6.5 R6_2.5.1 lifecycle_1.0.4
[33] bit_4.0.5 vroom_1.6.5 ragg_1.3.0 pkgconfig_2.0.3
[37] pillar_1.9.0 bslib_0.7.0 gtable_0.3.5 glue_1.7.0
[41] systemfonts_1.0.6 highr_0.10 xfun_0.43 tidyselect_1.2.1
[45] knitr_1.46 farver_2.1.1 htmltools_0.5.8.1 labeling_0.4.3
-[49] rmarkdown_2.26 compiler_4.4.0
+[49] rmarkdown_2.26 compiler_4.4.1
-LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdDIiCmF1dGhvcjogIkNDREwgZm9yIEFMU0YiCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBMb2FkIGFuZCB1c2UgUiBwYWNrYWdlcwotIFJlYWQgaW4gYW5kIHBlcmZvcm0gc2ltcGxlIG1hbmlwdWxhdGlvbnMgb2YgZGF0YSBmcmFtZXMKLSBVc2UgYGdncGxvdDJgIHRvIHBsb3QgYW5kIHZpc3VhbGl6ZSBkYXRhCi0gQ3VzdG9taXplIHBsb3RzIHVzaW5nIGZlYXR1cmVzIG9mIGBnZ3Bsb3QyYAoKLS0tCgpXZSdsbCB1c2UgYSByZWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHRvIGdldCBjb21mb3J0YWJsZSBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdXNpbmcgZ2dwbG90Mi4KV2UndmUgW3BlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlc10oLi9zY3JpcHRzLzAwLXNldHVwLWludHJvLXRvLVIuUikgb24gYSBwcmUtcHJvY2Vzc2VkIFthc3Ryb2N5dG9tYSBtaWNyb2FycmF5IGRhdGFzZXRdKGh0dHBzOi8vd3d3LnJlZmluZS5iaW8vZXhwZXJpbWVudHMvR1NFNDQ5NzEvZ2VuZS1leHByZXNzaW9uLWRhdGEtZnJvbS1waWxvY3l0aWMtYXN0cm9jeXRvbWEtdHVtb3VyLXNhbXBsZXMtYW5kLW5vcm1hbC1jZXJlYmVsbHVtLWNvbnRyb2xzKS4KV2UnbGwgc3RhcnQgYnkgbWFraW5nIGEgdm9sY2FubyBwbG90IG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBmcm9tIHRoaXMgZXhwZXJpbWVudC4KV2UgcGVyZm9ybWVkIHRocmVlIHNldHMgb2YgY29udHJhc3RzOgoKMSkgYHNleGAgY2F0ZWdvcnkgY29udHJhc3Rpbmc6IGBNYWxlYCB2cyBgRmVtYWxlYAoyKSBgdGlzc3VlYCBjYXRlZ29yeSBjb250cmFzdGluZyA6IGBQaWxvY3l0aWMgYXN0cm9jeXRvbWEgdHVtb3JgIHNhbXBsZXMgdnMgYG5vcm1hbCBjZXJlYmVsbHVtYCBzYW1wbGVzCjMpIEFuIGludGVyYWN0aW9uIG9mIGJvdGggYHNleGAgYW5kIGB0aXNzdWVgLgoKKipNb3JlIGdncGxvdDIgcmVzb3VyY2VzOioqCgotIFtnZ3Bsb3QyIHdlYnNpdGVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykKLSBbSGFuZHkgY2hlYXRzaGVldCBmb3IgZ2dwbG90MiAocGRmKV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS12aXN1YWxpemF0aW9uLnBkZikKLSBbX0RhdGEgVmlzdWFsaXphdGlvbiwgQSBwcmFjdGljYWwgaW50cm9kdWN0aW9uX10oaHR0cHM6Ly9zb2N2aXouY28vKQotIFtEYXRhIHZpc3VhbGl6YXRpb24gY2hhcHRlciBvZiBfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbCkKLSBbZ2dwbG90MiBvbmxpbmUgdHV0b3JpYWxdKGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vQ29tcGxldGUtR2dwbG90Mi1UdXRvcmlhbC1QYXJ0MS1XaXRoLVItQ29kZS5odG1sKQoKIyMgU2V0IFVwCgpXZSBzYXZlZCB0aGVzZSByZXN1bHRzIHRvIGEgdGFiIHNlcGFyYXRlZCB2YWx1ZXMgKFRTVikgZmlsZSBjYWxsZWQgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgLgpJdCdzIGJlZW4gc2F2ZWQgdG8gdGhlIGBkYXRhYCBmb2xkZXIuCkZpbGUgcGF0aHMgYXJlIHJlbGF0aXZlIHRvIHdoZXJlIHRoaXMgbm90ZWJvb2sgZmlsZSAoLlJtZCkgaXMgc2F2ZWQuClNvIHdlIGNhbiByZWZlcmVuY2UgaXQgbGF0ZXIsIGxldCdzIG1ha2UgYSB2YXJpYWJsZSB3aXRoIG91ciBkYXRhIGRpcmVjdG9yeSBuYW1lLgoKYGBge3J9CmRhdGFfZGlyIDwtICJkYXRhIgpgYGAKCkxldCdzIGRlY2xhcmUgb3VyIG91dHB1dCBmb2xkZXIgbmFtZSBhcyBpdHMgb3duIHZhcmlhYmxlLgoKYGBge3J9CnBsb3RzX2RpciA8LSAicGxvdHMiCmBgYAoKV2UgY2FuIGFsc28gY3JlYXRlIGEgZGlyZWN0b3J5IGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdC4KClRoZXJlJ3MgYSBjb3VwbGUgd2F5cyB0aGF0IHdlIGNhbiBjcmVhdGUgdGhhdCBkaXJlY3RvcnkgZnJvbSB3aXRoaW4gUi4KT25lIHdheSBpcyB0byB1c2UgdGhlIGJhc2UgUiBmdW5jdGlvbiBgZGlyLmNyZWF0ZSgpYCwgd2hpY2ggKGFzIHRoZSBuYW1lIHN1Z2dlc3RzKSB3aWxsIGNyZWF0ZSBhIGRpcmVjdG9yeS4KQnV0IHRoaXMgZnVuY3Rpb24gYXNzdW1lcyB0aGF0IHRoZSBkaXJlY3RvcnkgZG9lcyBub3QgeWV0IGV4aXN0LCBhbmQgaXQgd2lsbCB0aHJvdyBhbiBlcnJvciBpZiB5b3UgdHJ5IHRvIGNyZWF0ZSBhIGRpcmVjdG9yeSB0aGF0IGFscmVhZHkgZXhpc3RzLgpUbyBhdm9pZCB0aGlzIGVycm9yLCB3ZSBjYW4gcGxhY2UgdGhlIGRpcmVjdG9yeSBjcmVhdGlvbiBpbnNpZGUgYW4gYGlmYCBzdGF0ZW1lbnQsIHNvIHRoZSBjb2RlIHdpbGwgb25seSBydW4gaWYgdGhlIGRpcmVjdG9yeSBkb2VzIG5vdCB5ZXQgZXhpc3Q6CgpgYGB7ciBjcmVhdGVpZn0KIyBUaGUgaWYgc3RhdGVtZW50IGhlcmUgdGVzdHMgd2hldGhlciB0aGUgcGxvdCBkaXJlY3RvcnkgZXhpc3RzIGFuZAojIG9ubHkgZXhlY3V0ZXMgdGhlIGV4cHJlc3Npb25zIGJldHdlZW4gdGhlIGJyYWNlcyBpZiBpdCBkb2VzIG5vdC4KaWYgKCFkaXIuZXhpc3RzKHBsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHBsb3RzX2RpcikKfQpgYGAKCkluIHRoaXMgbm90ZWJvb2sgd2Ugd2lsbCBiZSB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgVGlkeXZlcnNlIHNldCBvZiBwYWNrYWdlcywgc28gd2UgbmVlZCB0byBsb2FkIGluIHRob3NlIGZ1bmN0aW9ucyB1c2luZyBgbGlicmFyeSgpYC4KV2UgY291bGQgbG9hZCB0aGUgaW5kaXZpZHVhbCBwYWNrYWdlcyB3ZSBuZWVkIG9uZSBhdCBhIHRpbWUsIGJ1dCBpdCBpcyBjb252ZW5pZW50IGZvciBub3cgdG8gbG9hZCB0aGVtIGFsbCB3aXRoIHRoZSBgdGlkeXZlcnNlYCAicGFja2FnZSwiIHdoaWNoIGdyb3VwcyBtYW55IG9mIHRoZW0gdG9nZXRoZXIgYXMgYSBzaG9ydGN1dC4KS2VlcCBhIGxvb2sgb3V0IGZvciB3aGVyZSB3ZSB0ZWxsIHlvdSB3aGljaCBpbmRpdmlkdWFsIHBhY2thZ2UgZGlmZmVyZW50IGZ1bmN0aW9ucyBjb21lIGZyb20uCgpgYGB7ciB0aWR5dmVyc2V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIFJlYWQgaW4gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHJlc3VsdHMgZmlsZQoKSGVyZSB3ZSBhcmUgdXNpbmcgYSBgdGlkeXZlcnNlYCBmdW5jdGlvbiBgcmVhZF90c3YoKWAgZnJvbSB0aGUgYHJlYWRyYCBwYWNrYWdlLgpMaWtlIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgbm90ZWJvb2ssIHdlIHdpbGwgc3RvcmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFzIGBzdGF0c19kZmAuCgpgYGB7ciByZWFkLXN0YXRzfQojIHJlYWQgaW4gdGhlIGZpbGUgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGZyb20gdGhlIGRhdGEgZGlyZWN0b3J5CnN0YXRzX2RmIDwtIHJlYWRfdHN2KGZpbGUucGF0aCgKICBkYXRhX2RpciwKICAiZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdiIKKSkKYGBgCgpXZSBjYW4gdGFrZSBhIGxvb2sgYXQgYSBjb2x1bW4gaW5kaXZpZHVhbGx5IGJ5IHVzaW5nIGEgYCRgLgpOb3RlIHdlIGFyZSB1c2luZyBgaGVhZCgpYCBzbyB0aGUgd2hvbGUgdGhpbmcgZG9lc24ndCBwcmludCBvdXQuCgpgYGB7ciBjb2x1bW59CmhlYWQoc3RhdHNfZGYkY29udHJhc3QpCmBgYAoKSWYgd2Ugd2FudCB0byBzZWUgYSBzcGVjaWZpYyBzZXQgb2YgdmFsdWVzLCB3ZSBjYW4gdXNlIGJyYWNrZXRzIHdpdGggdGhlIGluZGljZXMgb2YgdGhlIHZhbHVlcyB3ZSdkIGxpa2UgcmV0dXJuZWQuCgpgYGB7cn0Kc3RhdHNfZGYkYXZnX2V4cHJlc3Npb25bNjoxMF0KYGBgCgpMZXQncyBsb29rIGF0IHNvbWUgYmFzaWMgc3RhdGlzdGljcyBmcm9tIHRoZSBkYXRhIHNldCB1c2luZyBgc3VtbWFyeSgpYAoKYGBge3Igc3RhdHMtc3VtbWFyeSwgbGl2ZSA9IFRSVUV9CiMgc3VtbWFyeSBvZiBzdGF0c19kZgpzdW1tYXJ5KHN0YXRzX2RmKQpgYGAKClRoZSBzdGF0aXN0aWNzIGZvciBgY29udHJhc3RgIGFyZSBub3QgdmVyeSBpbmZvcm1hdGl2ZSwgc28gbGV0J3MgZG8gdGhhdCBhZ2FpbiB3aXRoIGp1c3QgdGhlIGBjb250cmFzdGAgY29sdW1uIGFmdGVyIGNvbnZlcnRpbmcgaXQgdG8gYSBgZmFjdG9yYApgYGB7ciBmYWN0b3Itc3VtbWFyeSwgbGl2ZSA9IFRSVUV9CiMgc3VtbWFyeSBvZiBgc3RhdHNfZGYkY29udHJhc3RgIGFzIGEgZmFjdG9yCnN1bW1hcnkoYXMuZmFjdG9yKHN0YXRzX2RmJGNvbnRyYXN0KSkKYGBgCgojIyBTZXQgdXAgdGhlIGRhdGFzZXQKCkJlZm9yZSB3ZSBtYWtlIG91ciBwbG90LCB3ZSB3YW50IHRvIGNhbGN1bGF0ZSBhIHNldCBvZiBuZXcgdmFsdWVzIGZvciBlYWNoIHJvdzsgdHJhbnNmb3JtYXRpb25zIG9mIHRoZSByYXcgc3RhdGlzdGljcyBpbiBvdXIgdGFibGUuClRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgYSBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgY2FsbGVkIGBtdXRhdGUoKWAgdG8gbWFrZSBhIG5ldyBjb2x1bW4gb2YgLWxvZzEwIHAgdmFsdWVzLgoKYGBge3IgbXV0YXRlfQojIGFkZCBhIGBuZWdfbG9nMTBfcGAgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lCnN0YXRzX2RmIDwtIG11dGF0ZShzdGF0c19kZiwgIyBkYXRhIGZyYW1lIHdlJ2QgbGlrZSB0byBhZGQgYSB2YXJpYWJsZSB0bwogIG5lZ19sb2cxMF9wID0gLWxvZzEwKHBfdmFsdWUpICMgY29sdW1uIG5hbWUgYW5kIHZhbHVlcwopCmBgYAoKTGV0J3MgZmlsdGVyIHRvIG9ubHkgYG1hbGVfZmVtYWxlYCBjb250cmFzdCBkYXRhLgpGaXJzdCBsZXQncyB0cnkgb3V0IGEgbG9naWNhbCBleHByZXNzaW9uOgoKYGBge3IgZXZhbCA9IEZBTFNFfQpzdGF0c19kZiRjb250cmFzdCA9PSAibWFsZV9mZW1hbGUiCmBgYAoKTm93IHdlIGNhbiB0cnkgb3V0IHRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uLgpOb3RpY2UgdGhhdCB3ZSBhcmUgbm90IGFzc2lnbmluZyB0aGUgcmVzdWx0cyB0byBhIHZhcmlhYmxlLCBzbyB0aGlzIGZpbHRlcmVkIGRhdGFzZXQgd2lsbCBub3QgYmUgc2F2ZWQgdG8gdGhlIGVudmlyb25tZW50LgoKYGBge3IgZmlsdGVyLCBsaXZlID0gVFJVRX0KIyBmaWx0ZXIgc3RhdHNfZGYgdG8gIm1hbGVfZmVtYWxlIiBvbmx5CmZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIikKYGBgCgpOb3cgd2UgY2FuIGFzc2lnbiB0aGUgcmVzdWx0cyB0byBhIG5ldyBkYXRhIGZyYW1lOiBgbWFsZV9mZW1hbGVfZGZgLgoKYGBge3IgZmlsdGVyLXNhdmUsIGxpdmUgPSBUUlVFfQojIGZpbHRlciBhbmQgc2F2ZSB0byBtYWxlX2ZlbWFsZV9kZgptYWxlX2ZlbWFsZV9kZiA8LSBmaWx0ZXIoc3RhdHNfZGYsIGNvbnRyYXN0ID09ICJtYWxlX2ZlbWFsZSIpCmBgYAoKIyMgUGxvdHRpbmcgdGhpcyBkYXRhCgpMZXQncyBtYWtlIGEgdm9sY2FubyBwbG90IHdpdGggdGhpcyBkYXRhLgpGaXJzdCBsZXQncyB0YWtlIGEgbG9vayBhdCBvbmx5IHRoZSB0dW1vciB2cy4gbm9ybWFsIGNvbXBhcmlzb24uCkxldCdzIHNhdmUgdGhpcyBhcyBhIHNlcGFyYXRlIGRhdGEgZnJhbWUgYnkgYXNzaWduaW5nIGl0IGEgbmV3IG5hbWUuCgpgYGB7ciBmaWx0ZXItdHVtb3J9CnR1bW9yX25vcm1hbF9kZiA8LSBmaWx0ZXIoc3RhdHNfZGYsIGNvbnRyYXN0ID09ICJhc3Ryb2N5dG9tYV9ub3JtYWwiKQpgYGAKClRvIG1ha2UgdGhpcyBwbG90IHdlIHdpbGwgYmUgdXNpbmcgZnVuY3Rpb25zIGZyb20gdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlLCB0aGUgbWFpbiBwbG90dGluZyBwYWNrYWdlIG9mIHRoZSB0aWR5dmVyc2UuCldlIHVzZSB0aGUgZmlyc3QgZnVuY3Rpb24sIGBnZ3Bsb3QoKWAgdG8gZGVmaW5lIHRoZSBkYXRhIHRoYXQgd2lsbCBiZSBwbG90dGVkLgpSZW1lbWJlciwgdGhlIG5hbWUgb2YgdGhpcyBwYWNrYWdlIGlzIGBnZ3Bsb3QyYCwgYnV0IHRoZSBmdW5jdGlvbiB3ZSB1c2UgaXMgY2FsbGVkIGBnZ3Bsb3QoKWAgd2l0aG91dCB0aGUgYDJgLgpgZ2dwbG90KClgIHRha2VzIHR3byBtYWluIGFyZ3VtZW50czoKCjEuIGBkYXRhYCwgd2hpY2ggaXMgdGhlIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUgZGF0YSB3ZSB3YW50IHRvIHBsb3QuCjIuIGBtYXBwaW5nYCwgd2hpY2ggaXMgYSBzcGVjaWFsIGxpc3QgbWFkZSB3aXRoIHRoZSBgYWVzKClgIGZ1bmN0aW9uIHRvIGRlc2NyaWJlIHdoaWNoIHZhbHVlcyB3aWxsIGJlIHVzZWQgZm9yIGVhY2ggKiphZXMqKnRoZXRpYyBjb21wb25lbnQgb2YgdGhlIHBsb3QsIHN1Y2ggYXMgdGhlIHggYW5kIHkgY29vcmRpbmF0ZXMgb2YgZWFjaCBwb2ludC4KKElmIHlvdSBmaW5kIGNhbGxpbmcgdGhpbmdzIGxpa2UgdGhlIHggYW5kIHkgY29vcmRpbmF0ZXMgImFlc3RoZXRpY3MiIGNvbmZ1c2luZywgZG9uJ3Qgd29ycnksIHlvdSBhcmUgbm90IGFsb25lLikKU3BlY2lmaWNhbGx5LCB0aGUgYGFlcygpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIHNwZWNpZnkgdGhhdCBhIGdpdmVuIGNvbHVtbiAodmFyaWFibGUpIGluIHlvdXIgZGF0YSBmcmFtZSBiZSBtYXBwZWQgdG8gYSBnaXZlbiBhZXN0aGV0aWMgY29tcG9uZW50IG9mIHRoZSBwbG90LgoKCmBgYHtyIGdncGxvdC1iYXNlfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLCAjIFRoaXMgZmlyc3QgYXJndW1lbnQgaXMgdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgZGF0YSB3ZSB3YW50IHRvIHBsb3QKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLCAjIFRoaXMgaXMgdGhlIGNvbHVtbiBuYW1lIG9mIHRoZSB2YWx1ZXMgd2Ugd2FudCB0byB1c2UKICAgICMgZm9yIHRoZSB4IGNvb3JkaW5hdGVzCiAgICB5ID0gbmVnX2xvZzEwX3AKICApICMgVGhpcyBpcyB0aGUgY29sdW1uIG5hbWUgb2YgdGhlIGRhdGEgd2Ugd2FudCBmb3IgdGhlIHktYXhpcwopCmBgYAoKWW91J2xsIG5vdGljZSB0aGlzIHBsb3QgZG9lc24ndCBoYXZlIGFueXRoaW5nIG9uIGl0IGJlY2F1c2Ugd2UgaGF2ZW4ndApzcGVjaWZpZWQgYSBwbG90IHR5cGUgeWV0LgpUbyBkbyB0aGF0LCB3ZSB3aWxsIGFkZCBhbm90aGVyIGdncGxvdCBsYXllciB3aXRoIGArYCB3aGljaCB3aWxsIHNwZWNpZnkgZXhhY3RseSB3aGF0IHdlIHdhbnQgdG8gcGxvdC4KQSB2b2xjYW5vIHBsb3QgaXMgYSBzcGVjaWFsIGtpbmQgb2Ygc2NhdHRlciBwbG90LCBzbyB0byBtYWtlIHRoYXQgd2Ugd2lsbCB3YW50IHRvIHBsb3QgaW5kaXZpZHVhbCBwb2ludHMsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIGBnZW9tX3BvaW50KClgLgoKYGBge3IgZ2dwbG90LXBvaW50cywgbGl2ZSA9IFRSVUV9CiMgVGhpcyBmaXJzdCBwYXJ0IGlzIHRoZSBzYW1lIGFzIGJlZm9yZQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AKICApCikgKwogICMgTm93IHdlIGFyZSBhZGRpbmcgb24gYSBsYXllciB0byBzcGVjaWZ5IHdoYXQga2luZCBvZiBwbG90IHdlIHdhbnQKICBnZW9tX3BvaW50KCkKYGBgCgpIZXJlJ3MgYSBicmllZiBzdW1tYXJ5IG9mIGdncGxvdDIgc3RydWN0dXJlLgohW2dncGxvdDIgc3RydWN0dXJlXShkaWFncmFtcy9nZ3Bsb3Rfc3RydWN0dXJlLnBuZykKCiMjIyBBZGp1c3Qgb3VyIGdncGxvdAoKTm93IHRoYXQgd2UgaGF2ZSBhIGJhc2UgcGxvdCB0aGF0IHNob3dzIG91ciBkYXRhLCB3ZSBjYW4gYWRkIGxheWVycyBvbiB0byBpdCBhbmQgYWRqdXN0IGl0LgpXZSBjYW4gYWRqdXN0IHRoZSBjb2xvciBvZiBwb2ludHMgdXNpbmcgdGhlIGBjb2xvcmAgYWVzdGhldGljLgoKYGBge3IgZ2dwbG90LWNvbG9yLCBsaXZlID0gVFJVRX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkgIyBXZSBhZGRlZCB0aGlzIGFyZ3VtZW50IHRvIGNvbG9yIGNvZGUgdGhlIHBvaW50cyEKKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKQmVjYXVzZSB3ZSBoYXZlIHNvIG1hbnkgcG9pbnRzIG92ZXJsYXBwaW5nIG9uZSBhbm90aGVyLCB3ZSB3aWxsIHdhbnQgdG8gYWRqdXN0CnRoZSB0cmFuc3BhcmVuY3ksIHdoaWNoIHdlIGNhbiBkbyB3aXRoIGFuIGBhbHBoYWAgYXJndW1lbnQuCgpgYGB7ciBnZ3Bsb3QtYWxwaGEsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSAjIFdlIGFyZSB1c2luZyB0aGUgYGFscGhhYCBhcmd1bWVudCB0byBtYWtlIG91ciBwb2ludHMgdHJhbnNwYXJlbnQKYGBgCgpOb3RpY2UgdGhhdCB3ZSBhZGRlZCB0aGUgYWxwaGEgd2l0aGluIHRoZSBgZ2VvbV9wb2ludCgpYCBmdW5jdGlvbiwgbm90IHRvIHRoZSBgYWVzKClgLgpXZSBkaWQgdGhpcyBiZWNhdXNlIHdlIHdhbnQgYWxsIG9mIHRoZSBwb2ludHMgdG8gaGF2ZSB0aGUgc2FtZSBsZXZlbCBvZiB0cmFuc3BhcmVuY3ksIGFuZCBpdCB3aWxsIG5vdCB2YXJ5IGRlcGVuZGluZyBvbiBhbnkgdmFyaWFibGUgaW4gdGhlIGRhdGEuCldlIGNhbiBhbHNvIGNoYW5nZSB0aGUgYmFja2dyb3VuZCBhbmQgYXBwZWFyYW5jZSBvZiB0aGUgcGxvdCBhcyBhIHdob2xlIGJ5IGFkZGluZyBhIGB0aGVtZWAuCgpgYGB7ciBnZ3Bsb3QtdGhlbWV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAjIEFkZCBvbiB0aGlzIHNldCBvZiBhcHBlYXJhbmNlIHByZXNldHMgdG8gbWFrZSBpdCBwcmV0dHkKICB0aGVtZV9idygpIApgYGAKCldlIGFyZSBub3QgbGltaXRlZCB0byBhIHNpbmdsZSBwbG90dGluZyBsYXllci4KRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnQgdG8gYWRkIGEgaG9yaXpvbnRhbCBsaW5lIHRvIGluZGljYXRlIGEgc2lnbmlmaWNhbmNlIGN1dG9mZiwgd2UgY2FuIGRvIHRoYXQgd2l0aCBgZ2VvbV9obGluZSgpYC4KRm9yIG5vdywgd2Ugd2lsbCBjaG9vc2UgdGhlIHZhbHVlIG9mIDUuNSAodGhhdCBpcyBjbG9zZSB0byBhIEJvbmZlcnJvbmkgY29ycmVjdGlvbikgYW5kIGFkZCB0aGF0IHRvIHRoZSBwbG90LgoKYGBge3IgZ2dwbG90LWhsaW5lLCBsaXZlID0gVFJVRX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgIyB3ZSBjYW4gc3BlY2lmeSBjb2xvcnMgYnkgbmFtZXMgaGVyZQpgYGAKCldlIGNhbiBjaGFuZ2UgdGhlIHggYW5kIHkgbGFiZWxzIHVzaW5nIGEgZmV3IGRpZmZlcmVudCBzdHJhdGVnaWVzLgpPbmUgYXBwcm9hY2ggaXMgdG8gdXNlIGZ1bmN0aW9ucyBgeGxhYigpYCBhbmQgYHlsYWIoKWAgaW5kaXZpZHVhbGx5IHRvIHNldCwgcmVzcGVjdGl2ZWx5LCB0aGUgeC1heGlzIGxhYmVsIGFuZCB0aGUgdGhlIHktYXhpcyBsYWJlbC4KCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtMX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogICMgQWRkIGxhYmVscyB3aXRoIHNlcGFyYXRlIGZ1bmN0aW9uczoKICB4bGFiKCJsb2cyIEZvbGQgQ2hhbmdlIFR1bW9yL05vcm1hbCIpICsKICB5bGFiKCItbG9nMTAgcCB2YWx1ZSIpCmBgYAoKCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgdGhlIGBnZ3Bsb3QyYCBmdW5jdGlvbiBgbGFicygpYCwgd2hpY2ggdGFrZXMgaW5kaXZpZHVhbCBhcmd1bWVudHMgZm9yIGVhY2ggbGFiZWwgd2Ugd2FudCB3YW50IHRvIHNldC4KV2UgY2FuIGFsc28gaW5jbHVkZSB0aGUgYXJndW1lbnQgYHRpdGxlYCB0byBhZGQgYW4gb3ZlcmFsbCBwbG90IHRpdGxlLgoKYGBge3IgZ2dwbG90LWxhYmVsLTIsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgeCBhbmQgeSBsYWJlbHMgYW5kIG92ZXJhbGwgcGxvdCB0aXRsZSB3aXRoIGFyZ3VtZW50cyB0byBsYWJzKCk6CiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBUdW1vci9Ob3JtYWwiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICB0aXRsZSA9ICJBc3Ryb2N5dG9tYSBUdW1vciB2cyBOb3JtYWwgQ2VyZWJlbGx1bSIKICApCgpgYGAKClNvbWV0aGluZyBncmVhdCBhYm91dCB0aGUgYGxhYnMoKWAgZnVuY3Rpb24gaXMgeW91IGNhbiBhbHNvIHVzZSBpdCB0byBzcGVjaWZ5IGxhYmVscyBmb3IgeW91ciAqbGVnZW5kcyogZGVyaXZlZCBmcm9tIGNlcnRhaW4gYWVzdGhldGljcy4KSW4gdGhpcyBwbG90LCBvdXIgbGVnZW5kIGlzIGRlcml2ZWQgZnJvbSBhICpjb2xvciBhZXN0aGV0aWMqLCBzbyB3ZSBjYW4gc3BlY2lmeSB0aGUga2V5d29yZCAiY29sb3IiIHRvIHVwZGF0ZSB0aGUgbGVnZW5kIHRpdGxlLgoKYGBge3IgZ2dwbG90LWxhYmVsLWFlc30KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogICMgQWRkIHggYW5kIHkgbGFiZWxzIGFuZCBvdmVyYWxsIHBsb3QgdGl0bGUgKGFuZCBtb3JlISkgd2l0aCBhcmd1bWVudHMgdG8gbGFicygpOgogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UgVHVtb3IvTm9ybWFsIiwKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgdGl0bGUgPSAiQXN0cm9jeXRvbWEgVHVtb3IgdnMgTm9ybWFsIENlcmViZWxsdW0iLAogICAgIyBVc2UgdGhlIGNvbG9yIGtleXdvcmQgdG8gbGFiZWwgdGhlIGNvbG9yIGxlZ2VuZAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkKCmBgYAoKClVzZSB0aGlzIGNodW5rIHRvIG1ha2UgdGhlIHNhbWUga2luZCBvZiBwbG90IGFzIHRoZSBwcmV2aW91cyBjaHVuayBidXQgaW5zdGVhZCBwbG90IHRoZSBtYWxlIGZlbWFsZSBjb250cmFzdCBkYXRhLCB0aGF0IGlzIHN0b3JlZCBpbiBgbWFsZV9mZW1hbGVfZGZgLgoKYGBge3IgbWYtdm9sY2FubywgbGl2ZSA9IFRSVUV9CiMgVXNlIHRoaXMgY2h1bmsgdG8gbWFrZSB0aGUgc2FtZSBraW5kIG9mIHZvbGNhbm8gcGxvdCwgYnV0IHdpdGggdGhlIG1hbGUtZmVtYWxlIGNvbnRyYXN0IGRhdGEuCmdncGxvdCgKICBtYWxlX2ZlbWFsZV9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UgTWFsZS9GZW1hbGUiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKQpgYGAKCgpUdXJucyBvdXQsIHdlIGRvbid0IGhhdmUgdG8gcGxvdCBlYWNoIGNvbnRyYXN0IHNlcGFyYXRlbHksIGluc3RlYWQsIHdlIGNhbiB1c2UgdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyBhbGwgdGhyZWUgY29udHJhc3RzJyBkYXRhLCBgc3RhdHNfZGZgLCBhbmQgYWRkIGEgYGZhY2V0X3dyYXBgIHRvIG1ha2UgZWFjaCBjb250cmFzdCBpdHMgb3duIHBsb3QuCgpgYGB7ciBnZ3Bsb3QtZmFjZXRzfQpnZ3Bsb3QoCiAgc3RhdHNfZGYsICMgU3dpdGNoIHRvIHRoZSBiaWdnZXIgZGF0YSBmcmFtZSB3aXRoIGFsbCB0aHJlZSBjb250cmFzdHMnIGRhdGEKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGZhY2V0X3dyYXAodmFycyhjb250cmFzdCkpICsKICBsYWJzKAogICAgIyBOb3cgdGhhdCB0aGlzIGluY2x1ZGVzIHRoZSBvdGhlciBjb250cmFzdHMsCiAgICAjIHdlJ2xsIG1ha2UgdGhlIHgtYXhpcyBsYWJlbCBtb3JlIGdlbmVyYWwKICAgIHggID0gImxvZzIgRm9sZCBDaGFuZ2UiLCAKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMjUsIDI1KSkgIyB6b29tIGluIG9uIHRoZSB4LWF4aXMKYGBgCgpXZSBjYW4gc3RvcmUgdGhlIHBsb3QgYXMgYW4gb2JqZWN0IGluIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQgYnkgdXNpbmcgYDwtYCBvcGVyYXRvci4KSGVyZSB3ZSB3aWxsIGNhbGwgdGhpcyBgdm9sY2Fub19wbG90YC4KCmBgYHtyIGdncGxvdC1zdG9yZS1vYmplY3R9CiMgV2UgYXJlIHNhdmluZyB0aGlzIHBsb3QgdG8gYSB2YXJpYWJsZSBuYW1lZCBgdm9sY2Fub19wbG90YAp2b2xjYW5vX3Bsb3QgPC0gZ2dwbG90KAogIHN0YXRzX2RmLCAKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHRoZW1lX2J3KCkgKwogIGZhY2V0X3dyYXAodmFycyhjb250cmFzdCkpICsKICBsYWJzKAogICAgeCA9ICJsb2cyIEZvbGQgQ2hhbmdlIiwKICAgIHkgPSAiLWxvZzEwIHAgdmFsdWUiLAogICAgY29sb3IgPSAiQXZlcmFnZSBleHByZXNzaW9uIgogICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMjUsIDI1KSkKYGBgCgpXaGVuIHdlIGFyZSBoYXBweSB3aXRoIG91ciBwbG90LCB3ZSBjYW4gc2F2ZSB0aGUgcGxvdCB1c2luZyBgZ2dzYXZlYC4KSXQncyBhIGdvb2QgaWRlYSB0byBhbHNvIHNwZWNpZnkgYHdpZHRoYCBhbmQgYGhlaWdodGAgYXJndW1lbnRzICh1bml0cyBpbiBpbmNoZXMpCnRvIGVuc3VyZSB0aGUgc2F2ZWQgcGxvdCBpcyBhbHdheXMgdGhlIHNhbWUgc2l6ZSBldmVyeSB0aW1lIHlvdSBydW4gdGhpcyBjb2RlLgpIZXJlLCB3ZSdsbCBzYXZlIGEgNiJ4NiIgcGxvdC4KCgpgYGB7ciBnZ3NhdmV9Cmdnc2F2ZSgKICBwbG90ID0gdm9sY2Fub19wbG90LAogIGZpbGVuYW1lID0gZmlsZS5wYXRoKHBsb3RzX2RpciwgInZvbGNhbm9fcGxvdC5wbmciKSwKICB3aWR0aCA9IDYsCiAgaGVpZ2h0ID0gNgopCmBgYAoKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CiMgUHJpbnQgb3V0IHRoZSB2ZXJzaW9ucyBhbmQgcGFja2FnZXMgd2UgYXJlIHVzaW5nIGluIHRoaXMgc2Vzc2lvbgpzZXNzaW9uSW5mbygpCmBgYAo=
+LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdDIiCmF1dGhvcjogIkNDREwgZm9yIEFMU0YiCmRhdGU6IDIwMjEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBMb2FkIGFuZCB1c2UgUiBwYWNrYWdlcwotIFJlYWQgaW4gYW5kIHBlcmZvcm0gc2ltcGxlIG1hbmlwdWxhdGlvbnMgb2YgZGF0YSBmcmFtZXMKLSBVc2UgYGdncGxvdDJgIHRvIHBsb3QgYW5kIHZpc3VhbGl6ZSBkYXRhCi0gQ3VzdG9taXplIHBsb3RzIHVzaW5nIGZlYXR1cmVzIG9mIGBnZ3Bsb3QyYAoKLS0tCgpXZSdsbCB1c2UgYSByZWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHRvIGdldCBjb21mb3J0YWJsZSBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdXNpbmcgZ2dwbG90Mi4KV2UndmUgW3BlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlc10oLi9zY3JpcHRzLzAwLXNldHVwLWludHJvLXRvLVIuUikgb24gYSBwcmUtcHJvY2Vzc2VkIFthc3Ryb2N5dG9tYSBtaWNyb2FycmF5IGRhdGFzZXRdKGh0dHBzOi8vd3d3LnJlZmluZS5iaW8vZXhwZXJpbWVudHMvR1NFNDQ5NzEvZ2VuZS1leHByZXNzaW9uLWRhdGEtZnJvbS1waWxvY3l0aWMtYXN0cm9jeXRvbWEtdHVtb3VyLXNhbXBsZXMtYW5kLW5vcm1hbC1jZXJlYmVsbHVtLWNvbnRyb2xzKS4KV2UnbGwgc3RhcnQgYnkgbWFraW5nIGEgdm9sY2FubyBwbG90IG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBmcm9tIHRoaXMgZXhwZXJpbWVudC4KV2UgcGVyZm9ybWVkIHRocmVlIHNldHMgb2YgY29udHJhc3RzOgoKMSkgYHNleGAgY2F0ZWdvcnkgY29udHJhc3Rpbmc6IGBNYWxlYCB2cyBgRmVtYWxlYAoyKSBgdGlzc3VlYCBjYXRlZ29yeSBjb250cmFzdGluZyA6IGBQaWxvY3l0aWMgYXN0cm9jeXRvbWEgdHVtb3JgIHNhbXBsZXMgdnMgYG5vcm1hbCBjZXJlYmVsbHVtYCBzYW1wbGVzCjMpIEFuIGludGVyYWN0aW9uIG9mIGJvdGggYHNleGAgYW5kIGB0aXNzdWVgLgoKKipNb3JlIGdncGxvdDIgcmVzb3VyY2VzOioqCgotIFtnZ3Bsb3QyIHdlYnNpdGVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykKLSBbSGFuZHkgY2hlYXRzaGVldCBmb3IgZ2dwbG90MiAocGRmKV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS12aXN1YWxpemF0aW9uLnBkZikKLSBbX0RhdGEgVmlzdWFsaXphdGlvbiwgQSBwcmFjdGljYWwgaW50cm9kdWN0aW9uX10oaHR0cHM6Ly9zb2N2aXouY28vKQotIFtEYXRhIHZpc3VhbGl6YXRpb24gY2hhcHRlciBvZiBfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZGxleS5uei9kYXRhLXZpc3VhbGl6ZS5odG1sKQotIFtnZ3Bsb3QyIG9ubGluZSB0dXRvcmlhbF0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWwpCgojIyBTZXQgVXAKCldlIHNhdmVkIHRoZXNlIHJlc3VsdHMgdG8gYSB0YWIgc2VwYXJhdGVkIHZhbHVlcyAoVFNWKSBmaWxlIGNhbGxlZCBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAuCkl0J3MgYmVlbiBzYXZlZCB0byB0aGUgYGRhdGFgIGZvbGRlci4KRmlsZSBwYXRocyBhcmUgcmVsYXRpdmUgdG8gd2hlcmUgdGhpcyBub3RlYm9vayBmaWxlICguUm1kKSBpcyBzYXZlZC4KU28gd2UgY2FuIHJlZmVyZW5jZSBpdCBsYXRlciwgbGV0J3MgbWFrZSBhIHZhcmlhYmxlIHdpdGggb3VyIGRhdGEgZGlyZWN0b3J5IG5hbWUuCgpgYGB7cn0KZGF0YV9kaXIgPC0gImRhdGEiCmBgYAoKTGV0J3MgZGVjbGFyZSBvdXIgb3V0cHV0IGZvbGRlciBuYW1lIGFzIGl0cyBvd24gdmFyaWFibGUuCgpgYGB7cn0KcGxvdHNfZGlyIDwtICJwbG90cyIKYGBgCgpXZSBjYW4gYWxzbyBjcmVhdGUgYSBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0LgoKVGhlcmUncyBhIGNvdXBsZSB3YXlzIHRoYXQgd2UgY2FuIGNyZWF0ZSB0aGF0IGRpcmVjdG9yeSBmcm9tIHdpdGhpbiBSLgpPbmUgd2F5IGlzIHRvIHVzZSB0aGUgYmFzZSBSIGZ1bmN0aW9uIGBkaXIuY3JlYXRlKClgLCB3aGljaCAoYXMgdGhlIG5hbWUgc3VnZ2VzdHMpIHdpbGwgY3JlYXRlIGEgZGlyZWN0b3J5LgpCdXQgdGhpcyBmdW5jdGlvbiBhc3N1bWVzIHRoYXQgdGhlIGRpcmVjdG9yeSBkb2VzIG5vdCB5ZXQgZXhpc3QsIGFuZCBpdCB3aWxsIHRocm93IGFuIGVycm9yIGlmIHlvdSB0cnkgdG8gY3JlYXRlIGEgZGlyZWN0b3J5IHRoYXQgYWxyZWFkeSBleGlzdHMuClRvIGF2b2lkIHRoaXMgZXJyb3IsIHdlIGNhbiBwbGFjZSB0aGUgZGlyZWN0b3J5IGNyZWF0aW9uIGluc2lkZSBhbiBgaWZgIHN0YXRlbWVudCwgc28gdGhlIGNvZGUgd2lsbCBvbmx5IHJ1biBpZiB0aGUgZGlyZWN0b3J5IGRvZXMgbm90IHlldCBleGlzdDoKCmBgYHtyIGNyZWF0ZWlmfQojIFRoZSBpZiBzdGF0ZW1lbnQgaGVyZSB0ZXN0cyB3aGV0aGVyIHRoZSBwbG90IGRpcmVjdG9yeSBleGlzdHMgYW5kCiMgb25seSBleGVjdXRlcyB0aGUgZXhwcmVzc2lvbnMgYmV0d2VlbiB0aGUgYnJhY2VzIGlmIGl0IGRvZXMgbm90LgppZiAoIWRpci5leGlzdHMocGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUocGxvdHNfZGlyKQp9CmBgYAoKSW4gdGhpcyBub3RlYm9vayB3ZSB3aWxsIGJlIHVzaW5nIGZ1bmN0aW9ucyBmcm9tIHRoZSBUaWR5dmVyc2Ugc2V0IG9mIHBhY2thZ2VzLCBzbyB3ZSBuZWVkIHRvIGxvYWQgaW4gdGhvc2UgZnVuY3Rpb25zIHVzaW5nIGBsaWJyYXJ5KClgLgpXZSBjb3VsZCBsb2FkIHRoZSBpbmRpdmlkdWFsIHBhY2thZ2VzIHdlIG5lZWQgb25lIGF0IGEgdGltZSwgYnV0IGl0IGlzIGNvbnZlbmllbnQgZm9yIG5vdyB0byBsb2FkIHRoZW0gYWxsIHdpdGggdGhlIGB0aWR5dmVyc2VgICJwYWNrYWdlLCIgd2hpY2ggZ3JvdXBzIG1hbnkgb2YgdGhlbSB0b2dldGhlciBhcyBhIHNob3J0Y3V0LgpLZWVwIGEgbG9vayBvdXQgZm9yIHdoZXJlIHdlIHRlbGwgeW91IHdoaWNoIGluZGl2aWR1YWwgcGFja2FnZSBkaWZmZXJlbnQgZnVuY3Rpb25zIGNvbWUgZnJvbS4KCmBgYHtyIHRpZHl2ZXJzZX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgUmVhZCBpbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0cyBmaWxlCgpIZXJlIHdlIGFyZSB1c2luZyBhIGB0aWR5dmVyc2VgIGZ1bmN0aW9uIGByZWFkX3RzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCkxpa2Ugd2UgZGlkIGluIHRoZSBwcmV2aW91cyBub3RlYm9vaywgd2Ugd2lsbCBzdG9yZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgYXMgYHN0YXRzX2RmYC4KCmBgYHtyIHJlYWQtc3RhdHN9CiMgcmVhZCBpbiB0aGUgZmlsZSBgZ2VuZV9yZXN1bHRzX0dTRTQ0OTcxLnRzdmAgZnJvbSB0aGUgZGF0YSBkaXJlY3RvcnkKc3RhdHNfZGYgPC0gcmVhZF90c3YoZmlsZS5wYXRoKAogIGRhdGFfZGlyLAogICJnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2IgopKQpgYGAKCldlIGNhbiB0YWtlIGEgbG9vayBhdCBhIGNvbHVtbiBpbmRpdmlkdWFsbHkgYnkgdXNpbmcgYSBgJGAuCk5vdGUgd2UgYXJlIHVzaW5nIGBoZWFkKClgIHNvIHRoZSB3aG9sZSB0aGluZyBkb2Vzbid0IHByaW50IG91dC4KCmBgYHtyIGNvbHVtbn0KaGVhZChzdGF0c19kZiRjb250cmFzdCkKYGBgCgpJZiB3ZSB3YW50IHRvIHNlZSBhIHNwZWNpZmljIHNldCBvZiB2YWx1ZXMsIHdlIGNhbiB1c2UgYnJhY2tldHMgd2l0aCB0aGUgaW5kaWNlcyBvZiB0aGUgdmFsdWVzIHdlJ2QgbGlrZSByZXR1cm5lZC4KCmBgYHtyfQpzdGF0c19kZiRhdmdfZXhwcmVzc2lvbls2OjEwXQpgYGAKCkxldCdzIGxvb2sgYXQgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGZyb20gdGhlIGRhdGEgc2V0IHVzaW5nIGBzdW1tYXJ5KClgCgpgYGB7ciBzdGF0cy1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzdW1tYXJ5IG9mIHN0YXRzX2RmCnN1bW1hcnkoc3RhdHNfZGYpCmBgYAoKVGhlIHN0YXRpc3RpY3MgZm9yIGBjb250cmFzdGAgYXJlIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLCBzbyBsZXQncyBkbyB0aGF0IGFnYWluIHdpdGgganVzdCB0aGUgYGNvbnRyYXN0YCBjb2x1bW4gYWZ0ZXIgY29udmVydGluZyBpdCB0byBhIGBmYWN0b3JgCmBgYHtyIGZhY3Rvci1zdW1tYXJ5LCBsaXZlID0gVFJVRX0KIyBzdW1tYXJ5IG9mIGBzdGF0c19kZiRjb250cmFzdGAgYXMgYSBmYWN0b3IKc3VtbWFyeShhcy5mYWN0b3Ioc3RhdHNfZGYkY29udHJhc3QpKQpgYGAKCiMjIFNldCB1cCB0aGUgZGF0YXNldAoKQmVmb3JlIHdlIG1ha2Ugb3VyIHBsb3QsIHdlIHdhbnQgdG8gY2FsY3VsYXRlIGEgc2V0IG9mIG5ldyB2YWx1ZXMgZm9yIGVhY2ggcm93OyB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIHJhdyBzdGF0aXN0aWNzIGluIG91ciB0YWJsZS4KVG8gZG8gdGhpcyB3ZSB3aWxsIHVzZSBhIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSBjYWxsZWQgYG11dGF0ZSgpYCB0byBtYWtlIGEgbmV3IGNvbHVtbiBvZiAtbG9nMTAgcCB2YWx1ZXMuCgpgYGB7ciBtdXRhdGV9CiMgYWRkIGEgYG5lZ19sb2cxMF9wYCBjb2x1bW4gdG8gdGhlIGRhdGEgZnJhbWUKc3RhdHNfZGYgPC0gbXV0YXRlKHN0YXRzX2RmLCAjIGRhdGEgZnJhbWUgd2UnZCBsaWtlIHRvIGFkZCBhIHZhcmlhYmxlIHRvCiAgbmVnX2xvZzEwX3AgPSAtbG9nMTAocF92YWx1ZSkgIyBjb2x1bW4gbmFtZSBhbmQgdmFsdWVzCikKYGBgCgpMZXQncyBmaWx0ZXIgdG8gb25seSBgbWFsZV9mZW1hbGVgIGNvbnRyYXN0IGRhdGEuCkZpcnN0IGxldCdzIHRyeSBvdXQgYSBsb2dpY2FsIGV4cHJlc3Npb246CgpgYGB7ciBldmFsID0gRkFMU0V9CnN0YXRzX2RmJGNvbnRyYXN0ID09ICJtYWxlX2ZlbWFsZSIKYGBgCgpOb3cgd2UgY2FuIHRyeSBvdXQgdGhlIGBmaWx0ZXIoKWAgZnVuY3Rpb24uCk5vdGljZSB0aGF0IHdlIGFyZSBub3QgYXNzaWduaW5nIHRoZSByZXN1bHRzIHRvIGEgdmFyaWFibGUsIHNvIHRoaXMgZmlsdGVyZWQgZGF0YXNldCB3aWxsIG5vdCBiZSBzYXZlZCB0byB0aGUgZW52aXJvbm1lbnQuCgpgYGB7ciBmaWx0ZXIsIGxpdmUgPSBUUlVFfQojIGZpbHRlciBzdGF0c19kZiB0byAibWFsZV9mZW1hbGUiIG9ubHkKZmlsdGVyKHN0YXRzX2RmLCBjb250cmFzdCA9PSAibWFsZV9mZW1hbGUiKQpgYGAKCk5vdyB3ZSBjYW4gYXNzaWduIHRoZSByZXN1bHRzIHRvIGEgbmV3IGRhdGEgZnJhbWU6IGBtYWxlX2ZlbWFsZV9kZmAuCgpgYGB7ciBmaWx0ZXItc2F2ZSwgbGl2ZSA9IFRSVUV9CiMgZmlsdGVyIGFuZCBzYXZlIHRvIG1hbGVfZmVtYWxlX2RmCm1hbGVfZmVtYWxlX2RmIDwtIGZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIikKYGBgCgojIyBQbG90dGluZyB0aGlzIGRhdGEKCkxldCdzIG1ha2UgYSB2b2xjYW5vIHBsb3Qgd2l0aCB0aGlzIGRhdGEuCkZpcnN0IGxldCdzIHRha2UgYSBsb29rIGF0IG9ubHkgdGhlIHR1bW9yIHZzLiBub3JtYWwgY29tcGFyaXNvbi4KTGV0J3Mgc2F2ZSB0aGlzIGFzIGEgc2VwYXJhdGUgZGF0YSBmcmFtZSBieSBhc3NpZ25pbmcgaXQgYSBuZXcgbmFtZS4KCmBgYHtyIGZpbHRlci10dW1vcn0KdHVtb3Jfbm9ybWFsX2RmIDwtIGZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gImFzdHJvY3l0b21hX25vcm1hbCIpCmBgYAoKVG8gbWFrZSB0aGlzIHBsb3Qgd2Ugd2lsbCBiZSB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgYGdncGxvdDJgIHBhY2thZ2UsIHRoZSBtYWluIHBsb3R0aW5nIHBhY2thZ2Ugb2YgdGhlIHRpZHl2ZXJzZS4KV2UgdXNlIHRoZSBmaXJzdCBmdW5jdGlvbiwgYGdncGxvdCgpYCB0byBkZWZpbmUgdGhlIGRhdGEgdGhhdCB3aWxsIGJlIHBsb3R0ZWQuClJlbWVtYmVyLCB0aGUgbmFtZSBvZiB0aGlzIHBhY2thZ2UgaXMgYGdncGxvdDJgLCBidXQgdGhlIGZ1bmN0aW9uIHdlIHVzZSBpcyBjYWxsZWQgYGdncGxvdCgpYCB3aXRob3V0IHRoZSBgMmAuCmBnZ3Bsb3QoKWAgdGFrZXMgdHdvIG1haW4gYXJndW1lbnRzOgoKMS4gYGRhdGFgLCB3aGljaCBpcyB0aGUgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdC4KMi4gYG1hcHBpbmdgLCB3aGljaCBpcyBhIHNwZWNpYWwgbGlzdCBtYWRlIHdpdGggdGhlIGBhZXMoKWAgZnVuY3Rpb24gdG8gZGVzY3JpYmUgd2hpY2ggdmFsdWVzIHdpbGwgYmUgdXNlZCBmb3IgZWFjaCAqKmFlcyoqdGhldGljIGNvbXBvbmVudCBvZiB0aGUgcGxvdCwgc3VjaCBhcyB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyBvZiBlYWNoIHBvaW50LgooSWYgeW91IGZpbmQgY2FsbGluZyB0aGluZ3MgbGlrZSB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyAiYWVzdGhldGljcyIgY29uZnVzaW5nLCBkb24ndCB3b3JyeSwgeW91IGFyZSBub3QgYWxvbmUuKQpTcGVjaWZpY2FsbHksIHRoZSBgYWVzKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gc3BlY2lmeSB0aGF0IGEgZ2l2ZW4gY29sdW1uICh2YXJpYWJsZSkgaW4geW91ciBkYXRhIGZyYW1lIGJlIG1hcHBlZCB0byBhIGdpdmVuIGFlc3RoZXRpYyBjb21wb25lbnQgb2YgdGhlIHBsb3QuCgoKYGBge3IgZ2dwbG90LWJhc2V9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsICMgVGhpcyBmaXJzdCBhcmd1bWVudCBpcyB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsICMgVGhpcyBpcyB0aGUgY29sdW1uIG5hbWUgb2YgdGhlIHZhbHVlcyB3ZSB3YW50IHRvIHVzZQogICAgIyBmb3IgdGhlIHggY29vcmRpbmF0ZXMKICAgIHkgPSBuZWdfbG9nMTBfcAogICkgIyBUaGlzIGlzIHRoZSBjb2x1bW4gbmFtZSBvZiB0aGUgZGF0YSB3ZSB3YW50IGZvciB0aGUgeS1heGlzCikKYGBgCgpZb3UnbGwgbm90aWNlIHRoaXMgcGxvdCBkb2Vzbid0IGhhdmUgYW55dGhpbmcgb24gaXQgYmVjYXVzZSB3ZSBoYXZlbid0CnNwZWNpZmllZCBhIHBsb3QgdHlwZSB5ZXQuClRvIGRvIHRoYXQsIHdlIHdpbGwgYWRkIGFub3RoZXIgZ2dwbG90IGxheWVyIHdpdGggYCtgIHdoaWNoIHdpbGwgc3BlY2lmeSBleGFjdGx5IHdoYXQgd2Ugd2FudCB0byBwbG90LgpBIHZvbGNhbm8gcGxvdCBpcyBhIHNwZWNpYWwga2luZCBvZiBzY2F0dGVyIHBsb3QsIHNvIHRvIG1ha2UgdGhhdCB3ZSB3aWxsIHdhbnQgdG8gcGxvdCBpbmRpdmlkdWFsIHBvaW50cywgd2hpY2ggd2UgY2FuIGRvIHdpdGggYGdlb21fcG9pbnQoKWAuCgpgYGB7ciBnZ3Bsb3QtcG9pbnRzLCBsaXZlID0gVFJVRX0KIyBUaGlzIGZpcnN0IHBhcnQgaXMgdGhlIHNhbWUgYXMgYmVmb3JlCmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcAogICkKKSArCiAgIyBOb3cgd2UgYXJlIGFkZGluZyBvbiBhIGxheWVyIHRvIHNwZWNpZnkgd2hhdCBraW5kIG9mIHBsb3Qgd2Ugd2FudAogIGdlb21fcG9pbnQoKQpgYGAKCkhlcmUncyBhIGJyaWVmIHN1bW1hcnkgb2YgZ2dwbG90MiBzdHJ1Y3R1cmUuCiFbZ2dwbG90MiBzdHJ1Y3R1cmVdKGRpYWdyYW1zL2dncGxvdF9zdHJ1Y3R1cmUucG5nKQoKIyMjIEFkanVzdCBvdXIgZ2dwbG90CgpOb3cgdGhhdCB3ZSBoYXZlIGEgYmFzZSBwbG90IHRoYXQgc2hvd3Mgb3VyIGRhdGEsIHdlIGNhbiBhZGQgbGF5ZXJzIG9uIHRvIGl0IGFuZCBhZGp1c3QgaXQuCldlIGNhbiBhZGp1c3QgdGhlIGNvbG9yIG9mIHBvaW50cyB1c2luZyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMuCgpgYGB7ciBnZ3Bsb3QtY29sb3IsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKSAjIFdlIGFkZGVkIHRoaXMgYXJndW1lbnQgdG8gY29sb3IgY29kZSB0aGUgcG9pbnRzIQopICsKICBnZW9tX3BvaW50KCkKYGBgCgpCZWNhdXNlIHdlIGhhdmUgc28gbWFueSBwb2ludHMgb3ZlcmxhcHBpbmcgb25lIGFub3RoZXIsIHdlIHdpbGwgd2FudCB0byBhZGp1c3QKdGhlIHRyYW5zcGFyZW5jeSwgd2hpY2ggd2UgY2FuIGRvIHdpdGggYW4gYGFscGhhYCBhcmd1bWVudC4KCmBgYHtyIGdncGxvdC1hbHBoYSwgbGl2ZSA9IFRSVUV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICMgV2UgYXJlIHVzaW5nIHRoZSBgYWxwaGFgIGFyZ3VtZW50IHRvIG1ha2Ugb3VyIHBvaW50cyB0cmFuc3BhcmVudApgYGAKCk5vdGljZSB0aGF0IHdlIGFkZGVkIHRoZSBhbHBoYSB3aXRoaW4gdGhlIGBnZW9tX3BvaW50KClgIGZ1bmN0aW9uLCBub3QgdG8gdGhlIGBhZXMoKWAuCldlIGRpZCB0aGlzIGJlY2F1c2Ugd2Ugd2FudCBhbGwgb2YgdGhlIHBvaW50cyB0byBoYXZlIHRoZSBzYW1lIGxldmVsIG9mIHRyYW5zcGFyZW5jeSwgYW5kIGl0IHdpbGwgbm90IHZhcnkgZGVwZW5kaW5nIG9uIGFueSB2YXJpYWJsZSBpbiB0aGUgZGF0YS4KV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBiYWNrZ3JvdW5kIGFuZCBhcHBlYXJhbmNlIG9mIHRoZSBwbG90IGFzIGEgd2hvbGUgYnkgYWRkaW5nIGEgYHRoZW1lYC4KCmBgYHtyIGdncGxvdC10aGVtZX0KZ2dwbG90KAogIHR1bW9yX25vcm1hbF9kZiwKICBhZXMoCiAgICB4ID0gbG9nX2ZvbGRfY2hhbmdlLAogICAgeSA9IG5lZ19sb2cxMF9wLAogICAgY29sb3IgPSBhdmdfZXhwcmVzc2lvbgogICkKKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICMgQWRkIG9uIHRoaXMgc2V0IG9mIGFwcGVhcmFuY2UgcHJlc2V0cyB0byBtYWtlIGl0IHByZXR0eQogIHRoZW1lX2J3KCkgCmBgYAoKV2UgYXJlIG5vdCBsaW1pdGVkIHRvIGEgc2luZ2xlIHBsb3R0aW5nIGxheWVyLgpGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudCB0byBhZGQgYSBob3Jpem9udGFsIGxpbmUgdG8gaW5kaWNhdGUgYSBzaWduaWZpY2FuY2UgY3V0b2ZmLCB3ZSBjYW4gZG8gdGhhdCB3aXRoIGBnZW9tX2hsaW5lKClgLgpGb3Igbm93LCB3ZSB3aWxsIGNob29zZSB0aGUgdmFsdWUgb2YgNS41ICh0aGF0IGlzIGNsb3NlIHRvIGEgQm9uZmVycm9uaSBjb3JyZWN0aW9uKSBhbmQgYWRkIHRoYXQgdG8gdGhlIHBsb3QuCgpgYGB7ciBnZ3Bsb3QtaGxpbmUsIGxpdmUgPSBUUlVFfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSAjIHdlIGNhbiBzcGVjaWZ5IGNvbG9ycyBieSBuYW1lcyBoZXJlCmBgYAoKV2UgY2FuIGNoYW5nZSB0aGUgeCBhbmQgeSBsYWJlbHMgdXNpbmcgYSBmZXcgZGlmZmVyZW50IHN0cmF0ZWdpZXMuCk9uZSBhcHByb2FjaCBpcyB0byB1c2UgZnVuY3Rpb25zIGB4bGFiKClgIGFuZCBgeWxhYigpYCBpbmRpdmlkdWFsbHkgdG8gc2V0LCByZXNwZWN0aXZlbHksIHRoZSB4LWF4aXMgbGFiZWwgYW5kIHRoZSB0aGUgeS1heGlzIGxhYmVsLgoKCmBgYHtyIGdncGxvdC1sYWJlbC0xfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgbGFiZWxzIHdpdGggc2VwYXJhdGUgZnVuY3Rpb25zOgogIHhsYWIoImxvZzIgRm9sZCBDaGFuZ2UgVHVtb3IvTm9ybWFsIikgKwogIHlsYWIoIi1sb2cxMCBwIHZhbHVlIikKYGBgCgoKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSB0aGUgYGdncGxvdDJgIGZ1bmN0aW9uIGBsYWJzKClgLCB3aGljaCB0YWtlcyBpbmRpdmlkdWFsIGFyZ3VtZW50cyBmb3IgZWFjaCBsYWJlbCB3ZSB3YW50IHdhbnQgdG8gc2V0LgpXZSBjYW4gYWxzbyBpbmNsdWRlIHRoZSBhcmd1bWVudCBgdGl0bGVgIHRvIGFkZCBhbiBvdmVyYWxsIHBsb3QgdGl0bGUuCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtMiwgbGl2ZSA9IFRSVUV9CmdncGxvdCgKICB0dW1vcl9ub3JtYWxfZGYsCiAgYWVzKAogICAgeCA9IGxvZ19mb2xkX2NoYW5nZSwKICAgIHkgPSBuZWdfbG9nMTBfcCwKICAgIGNvbG9yID0gYXZnX2V4cHJlc3Npb24KICApCikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LjUsIGNvbG9yID0gImRhcmtncmVlbiIpICsKICB0aGVtZV9idygpICsKICAjIEFkZCB4IGFuZCB5IGxhYmVscyBhbmQgb3ZlcmFsbCBwbG90IHRpdGxlIHdpdGggYXJndW1lbnRzIHRvIGxhYnMoKToKICBsYWJzKAogICAgeCA9ICJsb2cyIEZvbGQgQ2hhbmdlIFR1bW9yL05vcm1hbCIsCiAgICB5ID0gIi1sb2cxMCBwIHZhbHVlIiwKICAgIHRpdGxlID0gIkFzdHJvY3l0b21hIFR1bW9yIHZzIE5vcm1hbCBDZXJlYmVsbHVtIgogICkKCmBgYAoKU29tZXRoaW5nIGdyZWF0IGFib3V0IHRoZSBgbGFicygpYCBmdW5jdGlvbiBpcyB5b3UgY2FuIGFsc28gdXNlIGl0IHRvIHNwZWNpZnkgbGFiZWxzIGZvciB5b3VyICpsZWdlbmRzKiBkZXJpdmVkIGZyb20gY2VydGFpbiBhZXN0aGV0aWNzLgpJbiB0aGlzIHBsb3QsIG91ciBsZWdlbmQgaXMgZGVyaXZlZCBmcm9tIGEgKmNvbG9yIGFlc3RoZXRpYyosIHNvIHdlIGNhbiBzcGVjaWZ5IHRoZSBrZXl3b3JkICJjb2xvciIgdG8gdXBkYXRlIHRoZSBsZWdlbmQgdGl0bGUuCgpgYGB7ciBnZ3Bsb3QtbGFiZWwtYWVzfQpnZ3Bsb3QoCiAgdHVtb3Jfbm9ybWFsX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgIyBBZGQgeCBhbmQgeSBsYWJlbHMgYW5kIG92ZXJhbGwgcGxvdCB0aXRsZSAoYW5kIG1vcmUhKSB3aXRoIGFyZ3VtZW50cyB0byBsYWJzKCk6CiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBUdW1vci9Ob3JtYWwiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICB0aXRsZSA9ICJBc3Ryb2N5dG9tYSBUdW1vciB2cyBOb3JtYWwgQ2VyZWJlbGx1bSIsCiAgICAjIFVzZSB0aGUgY29sb3Iga2V5d29yZCB0byBsYWJlbCB0aGUgY29sb3IgbGVnZW5kCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKQoKYGBgCgoKVXNlIHRoaXMgY2h1bmsgdG8gbWFrZSB0aGUgc2FtZSBraW5kIG9mIHBsb3QgYXMgdGhlIHByZXZpb3VzIGNodW5rIGJ1dCBpbnN0ZWFkIHBsb3QgdGhlIG1hbGUgZmVtYWxlIGNvbnRyYXN0IGRhdGEsIHRoYXQgaXMgc3RvcmVkIGluIGBtYWxlX2ZlbWFsZV9kZmAuCgpgYGB7ciBtZi12b2xjYW5vLCBsaXZlID0gVFJVRX0KIyBVc2UgdGhpcyBjaHVuayB0byBtYWtlIHRoZSBzYW1lIGtpbmQgb2Ygdm9sY2FubyBwbG90LCBidXQgd2l0aCB0aGUgbWFsZS1mZW1hbGUgY29udHJhc3QgZGF0YS4KZ2dwbG90KAogIG1hbGVfZmVtYWxlX2RmLAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgbGFicygKICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSBNYWxlL0ZlbWFsZSIsCiAgICB5ID0gIi1sb2cxMCBwIHZhbHVlIiwKICAgIGNvbG9yID0gIkF2ZXJhZ2UgZXhwcmVzc2lvbiIKICApCmBgYAoKClR1cm5zIG91dCwgd2UgZG9uJ3QgaGF2ZSB0byBwbG90IGVhY2ggY29udHJhc3Qgc2VwYXJhdGVseSwgaW5zdGVhZCwgd2UgY2FuIHVzZSB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIGFsbCB0aHJlZSBjb250cmFzdHMnIGRhdGEsIGBzdGF0c19kZmAsIGFuZCBhZGQgYSBgZmFjZXRfd3JhcGAgdG8gbWFrZSBlYWNoIGNvbnRyYXN0IGl0cyBvd24gcGxvdC4KCmBgYHtyIGdncGxvdC1mYWNldHN9CmdncGxvdCgKICBzdGF0c19kZiwgIyBTd2l0Y2ggdG8gdGhlIGJpZ2dlciBkYXRhIGZyYW1lIHdpdGggYWxsIHRocmVlIGNvbnRyYXN0cycgZGF0YQogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgZmFjZXRfd3JhcCh2YXJzKGNvbnRyYXN0KSkgKwogIGxhYnMoCiAgICAjIE5vdyB0aGF0IHRoaXMgaW5jbHVkZXMgdGhlIG90aGVyIGNvbnRyYXN0cywKICAgICMgd2UnbGwgbWFrZSB0aGUgeC1heGlzIGxhYmVsIG1vcmUgZ2VuZXJhbAogICAgeCAgPSAibG9nMiBGb2xkIENoYW5nZSIsIAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0yNSwgMjUpKSAjIHpvb20gaW4gb24gdGhlIHgtYXhpcwpgYGAKCldlIGNhbiBzdG9yZSB0aGUgcGxvdCBhcyBhbiBvYmplY3QgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudCBieSB1c2luZyBgPC1gIG9wZXJhdG9yLgpIZXJlIHdlIHdpbGwgY2FsbCB0aGlzIGB2b2xjYW5vX3Bsb3RgLgoKYGBge3IgZ2dwbG90LXN0b3JlLW9iamVjdH0KIyBXZSBhcmUgc2F2aW5nIHRoaXMgcGxvdCB0byBhIHZhcmlhYmxlIG5hbWVkIGB2b2xjYW5vX3Bsb3RgCnZvbGNhbm9fcGxvdCA8LSBnZ3Bsb3QoCiAgc3RhdHNfZGYsIAogIGFlcygKICAgIHggPSBsb2dfZm9sZF9jaGFuZ2UsCiAgICB5ID0gbmVnX2xvZzEwX3AsCiAgICBjb2xvciA9IGF2Z19leHByZXNzaW9uCiAgKQopICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNS41LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgdGhlbWVfYncoKSArCiAgZmFjZXRfd3JhcCh2YXJzKGNvbnRyYXN0KSkgKwogIGxhYnMoCiAgICB4ID0gImxvZzIgRm9sZCBDaGFuZ2UiLAogICAgeSA9ICItbG9nMTAgcCB2YWx1ZSIsCiAgICBjb2xvciA9ICJBdmVyYWdlIGV4cHJlc3Npb24iCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0yNSwgMjUpKQpgYGAKCldoZW4gd2UgYXJlIGhhcHB5IHdpdGggb3VyIHBsb3QsIHdlIGNhbiBzYXZlIHRoZSBwbG90IHVzaW5nIGBnZ3NhdmVgLgpJdCdzIGEgZ29vZCBpZGVhIHRvIGFsc28gc3BlY2lmeSBgd2lkdGhgIGFuZCBgaGVpZ2h0YCBhcmd1bWVudHMgKHVuaXRzIGluIGluY2hlcykKdG8gZW5zdXJlIHRoZSBzYXZlZCBwbG90IGlzIGFsd2F5cyB0aGUgc2FtZSBzaXplIGV2ZXJ5IHRpbWUgeW91IHJ1biB0aGlzIGNvZGUuCkhlcmUsIHdlJ2xsIHNhdmUgYSA2Ing2IiBwbG90LgoKCmBgYHtyIGdnc2F2ZX0KZ2dzYXZlKAogIHBsb3QgPSB2b2xjYW5vX3Bsb3QsCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgocGxvdHNfZGlyLCAidm9sY2Fub19wbG90LnBuZyIpLAogIHdpZHRoID0gNiwKICBoZWlnaHQgPSA2CikKYGBgCgojIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0KIyBQcmludCBvdXQgdGhlIHZlcnNpb25zIGFuZCBwYWNrYWdlcyB3ZSBhcmUgdXNpbmcgaW4gdGhpcyBzZXNzaW9uCnNlc3Npb25JbmZvKCkKYGBgCg==
diff --git a/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd b/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd
index eeb867ea..a0414c65 100644
--- a/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd
+++ b/intro-to-R-tidyverse/03-intro_to_tidyverse-live.Rmd
@@ -27,7 +27,7 @@ It is a pre-processed [astrocytoma microarray dataset](https://www.refine.bio/ex
**More tidyverse resources:**
-- [R for Data Science](https://r4ds.had.co.nz/)
+- [R for Data Science](https://r4ds.hadley.nz/)
- [tidyverse documentation](https://tidyverse.org/)
- [`dplyr` documentation](https://dplyr.tidyverse.org/)
- [`readr` documentation](https://readr.tidyverse.org/)
@@ -175,7 +175,7 @@ What information is contained in `gene_df`?
One nifty feature that was added to `R` in version 4.1 is the pipe: `|>`.
Pipes are very handy things that allow you to funnel the result of one expression to the next, making your code more streamlined and fluently expressing the flow of data through a series of operations.
-_Note:_ If you are using a version of `R` prior to 4.1 (or looking at older code), pipe functionality was available through the `magrittr` package , which used a pipe that looked like this: `%>%`.
+_Note:_ If you are using a version of `R` prior to 4.1 (or looking at older code), pipe functionality was available through the `magrittr` package, which used a pipe that looked like this: `%>%`.
That pipe was the inspiration for the native R pipe we are using here.
While there are some minor differences, you can mostly treat them interchangeably as long as you load the `magrittr` package or `dplyr`, which also loads that version of the pipe.
diff --git a/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html b/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html
index b2459ee8..e04143a4 100644
--- a/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html
+++ b/intro-to-R-tidyverse/03-intro_to_tidyverse.nb.html
@@ -2903,7 +2903,7 @@ Objectives
on.
More tidyverse resources:
In this example, we will use canonical pathways which are (ref):
-Gene sets from pathway databases. Usually, these gene sets are canonical representations of a biological process compiled by domain experts.
+Gene sets from pathway databases. Usually, these gene sets are
+canonical representations of a biological process compiled by domain
+experts.
-And are a subset of C2: curated gene sets
. Specifically, we will use the KEGG (Kyoto Encyclopedia of Genes and Genomes) pathways.
+And are a subset of C2: curated gene sets
. Specifically,
+we will use the KEGG (Kyoto
+Encyclopedia of Genes and Genomes) pathways.
-
+
# Filter the mouse data frame to the KEGG pathways that are included in the
# curated gene sets
-mm_kegg_df <- mm_msigdb_df %>%
- dplyr::filter(gs_cat == "C2", # curated gene sets
- gs_subcat == "CP:KEGG") # KEGG pathways
+mm_kegg_df <- mm_msigdb_df |>
+ dplyr::filter(gs_cat == "C2", # curated gene sets
+ gs_subcat == "CP:KEGG") # KEGG pathways
-Note: We could specified that we wanted the KEGG gene sets using the category
and subcategory
arguments of msigdbr()
, but we’re going for general steps!
+Note: We could specified that we wanted the KEGG gene sets using
+the category
and subcategory
arguments of
+msigdbr()
, but we’re going for general steps!
colnames(mm_kegg_df)
-
+
[1] "gs_cat" "gs_subcat" "gs_name"
- [4] "entrez_gene" "gene_symbol" "human_entrez_gene"
- [7] "human_gene_symbol" "gs_id" "gs_pmid"
-[10] "gs_geoid" "gs_exact_source" "gs_url"
-[13] "gs_description" "species_name" "species_common_name"
-[16] "ortholog_sources" "num_ortholog_sources"
+ [4] "gene_symbol" "entrez_gene" "ensembl_gene"
+ [7] "human_gene_symbol" "human_entrez_gene" "human_ensembl_gene"
+[10] "gs_id" "gs_pmid" "gs_geoid"
+[13] "gs_exact_source" "gs_url" "gs_description"
+[16] "taxon_id" "ortholog_sources" "num_ortholog_sources"
-
+
gs_cat
-
gs_subcat
-
gs_name
-
-entrez_gene
-
gene_symbol
-
-human_entrez_gene
-
+entrez_gene
+ensembl_gene
human_gene_symbol
-
+human_entrez_gene
+human_ensembl_gene
gs_id
-
gs_pmid
-
gs_geoid
-
gs_exact_source
-
gs_url
-
gs_description
-
-species_name
-
-species_common_name
-
+taxon_id
ortholog_sources
-
num_ortholog_sources
-The clusterProfiler
function we will use requires a data frame with two columns, where one column contains the term identifier or name and one column contains gene identifiers that match our gene lists we want to check for enrichment. Our data frame with KEGG terms contains Entrez IDs and gene symbols.
+The clusterProfiler
function we will use requires a data
+frame with two columns, where one column contains the term identifier or
+name and one column contains gene identifiers that match our gene lists
+we want to check for enrichment. Our data frame with KEGG terms contains
+Entrez IDs and gene symbols.
Read in DGE results and prep
@@ -627,18 +3222,15 @@ Read in DGE results and prep
vs_low_df <- readr::read_tsv(vs_low_file)
-
-
+
+Rows: 35429 Columns: 7
── Column specification ────────────────────────────────────────────────────────
-cols(
- Gene = col_character(),
- baseMean = col_double(),
- log2FoldChange = col_double(),
- lfcSE = col_double(),
- stat = col_double(),
- pvalue = col_double(),
- padj = col_double()
-)
+Delimiter: "\t"
+chr (1): Gene
+dbl (6): baseMean, log2FoldChange, lfcSE, stat, pvalue, padj
+
+ℹ Use `spec()` to retrieve the full column specification for this data.
+ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
@@ -657,92 +3249,85 @@ Read in DGE results and prep
Gene identifier conversion
-Our data frame of DGE results contains Ensembl gene identifiers. So we will need to convert from these identifiers into either the gene symbols or Entrez IDs that are present in the data we extracted with msigdbr()
.
-We’re going to convert our identifiers to gene symbols because they are a bit more human readable, but you can, with the change of a single argument, use the same code to convert to many other types of identifiers!
-The annotation package org.Mm.eg.db
contains information for different identifiers. org.Mm.eg.db
is specific to Mus musculus – this is what the Mm
in the package name is referencing. To perform gene identifier conversion in human (Homo sapiens) we could use org.Hs.eg.db
; we would use org.Dr.eg.db
for zebrafish (Danio rerio).
-We can see what types of IDs are available to us in an annotation package with keytypes()
.
+Our data frame of DGE results contains Ensembl gene identifiers. So
+we will need to convert from these identifiers into either the gene
+symbols or Entrez IDs that are present in the data we extracted with
+msigdbr()
.
+We’re going to convert our identifiers to gene symbols because they
+are a bit more human readable, but you can, with the change of a single
+argument, use the same code to convert to many other types of
+identifiers!
+The annotation package org.Mm.eg.db
contains information
+for different identifiers. org.Mm.eg.db
is specific to
+Mus musculus – this is what the Mm
in the package
+name is referencing. To perform gene identifier conversion in human
+(Homo sapiens) we could use org.Hs.eg.db
; we would
+use org.Dr.eg.db
for zebrafish (Danio rerio).
+We can see what types of IDs are available to us in an annotation
+package with keytypes()
.
keytypes(org.Mm.eg.db)
-
+
[1] "ACCNUM" "ALIAS" "ENSEMBL" "ENSEMBLPROT" "ENSEMBLTRANS"
[6] "ENTREZID" "ENZYME" "EVIDENCE" "EVIDENCEALL" "GENENAME"
-[11] "GO" "GOALL" "IPI" "MGI" "ONTOLOGY"
-[16] "ONTOLOGYALL" "PATH" "PFAM" "PMID" "PROSITE"
-[21] "REFSEQ" "SYMBOL" "UNIGENE" "UNIPROT"
+[11] "GENETYPE" "GO" "GOALL" "IPI" "MGI"
+[16] "ONTOLOGY" "ONTOLOGYALL" "PATH" "PFAM" "PMID"
+[21] "PROSITE" "REFSEQ" "SYMBOL" "UNIPROT"
-
+
ACCNUM
-
ALIAS
-
ENSEMBL
-
ENSEMBLPROT
-
ENSEMBLTRANS
-
ENTREZID
-
ENZYME
-
EVIDENCE
-
EVIDENCEALL
-
GENENAME
-
+GENETYPE
GO
-
GOALL
-
IPI
-
MGI
-
ONTOLOGY
-
ONTOLOGYALL
-
PATH
-
PFAM
-
PMID
-
PROSITE
-
REFSEQ
-
SYMBOL
-
-UNIGENE
-
UNIPROT
-Even though we’ll use this package to convert from Ensembl gene IDs (ENSEMBL
) to gene symbols (SYMBOL
), we could just as easily use it to convert from an Ensembl transcript ID (ENSEMBLTRANS
) to Entrez IDs (ENTREZID
).
-The function we will use to map from Ensembl gene IDs to gene symbols is called mapIds()
.
+Even though we’ll use this package to convert from Ensembl gene IDs
+(ENSEMBL
) to gene symbols (SYMBOL
), we could
+just as easily use it to convert from an Ensembl transcript ID
+(ENSEMBLTRANS
) to Entrez IDs (ENTREZID
).
+The function we will use to map from Ensembl gene IDs to gene symbols
+is called mapIds()
.
-
+
# This returns a named vector which we can convert to a data frame, where
# the keys (Ensembl IDs) are the names
symbols_vector <- mapIds(org.Mm.eg.db, # Specify the annotation package
- # The vector of gene identifiers we want to
+ # The vector of gene identifiers we want to
# map
- keys = vs_low_df$Gene,
+ keys = vs_low_df$Gene,
# What type of gene identifiers we're starting
# with
- keytype = "ENSEMBL",
+ keytype = "ENSEMBL",
# The type of gene identifier we want returned
- column = "SYMBOL",
+ column = "SYMBOL",
# In the case of 1:many mappings, return the
# first one. This is default behavior!
- multiVals = "first")
+ multiVals = "first")
'select()' returned 1:many mapping between keys and columns
@@ -756,13 +3341,19 @@ Gene identifier conversion
-This message is letting us know that sometimes Ensembl gene identifiers will map to multiple gene symbols. In this case, it’s also possible that a gene symbol will map to multiple Ensembl IDs.
-Now we are ready to add the gene symbols to our data frame with the DGE results. We can use a join function from the dplyr
package to do this, which will use the Ensembl gene IDs in both data frames to determine how to join the rows.
-Let’s do this first for the comparison to the low stem cell capacity population.
+This message is letting us know that sometimes Ensembl gene
+identifiers will map to multiple gene symbols. In this case, it’s also
+possible that a gene symbol will map to multiple Ensembl IDs.
+Now we are ready to add the gene symbols to our data frame with the
+DGE results. We can use a join function from the
+dplyr
package to do this, which will use the Ensembl gene
+IDs in both data frames to determine how to join the rows.
+Let’s do this first for the comparison to the low stem cell capacity
+population.
-
-vs_low_df <- symbols_df %>%
+
+vs_low_df <- symbols_df |>
# An *inner* join will only return rows that are in both data frames
dplyr::inner_join(vs_low_df,
# The name of the column that contains the Ensembl gene IDs
@@ -774,37 +3365,43 @@ Gene identifier conversion
Drop NA
values
-Some of these rows have NA
values in padj
, which can happen for a number of reasons including when all samples have zero counts or a gene has low mean expression.
-Let’s filter to rows that do not have any NA
using a function tidyr::drop_na()
. This will also drop genes that have an Ensembl gene identifier but no gene symbol!
+Some of these rows have NA
values in padj
,
+which can
+happen for a number of reasons including when all samples have zero
+counts or a gene has low mean expression.
+Let’s filter to rows that do not have any NA
+using a function tidyr::drop_na()
. This will also drop
+genes that have an Ensembl gene identifier but no gene symbol!
-
-# Remove rows that are not complete (e.g., contain NAs) by filtering to only
+
+# Remove rows that are not complete (e.g., contain NAs) by filtering to only
# complete rows
-vs_low_df <- vs_low_df %>%
+vs_low_df <- vs_low_df |>
tidyr::drop_na()
-Now we’ll read in our data frame of DGE results from another comparison. To save us some time during instruction, we’ve already done the gene identifier conversion and filtering to remove NA
values in this notebook. We took a different series of steps to achieve the same thing, which is often possible in R!
+Now we’ll read in our data frame of DGE results from another
+comparison. To save us some time during instruction, we’ve
+already done the gene identifier conversion and filtering to remove
+NA
values in this
+notebook. We took a different series of steps to achieve the same
+thing, which is often possible in R!
vs_unsorted_df <- readr::read_tsv(vs_unsorted_file)
-
-
+
+Rows: 17151 Columns: 8
── Column specification ────────────────────────────────────────────────────────
-cols(
- Gene = col_character(),
- baseMean = col_double(),
- log2FoldChange = col_double(),
- lfcSE = col_double(),
- stat = col_double(),
- pvalue = col_double(),
- padj = col_double(),
- gene_symbol = col_character()
-)
+Delimiter: "\t"
+chr (2): Gene, gene_symbol
+dbl (6): baseMean, log2FoldChange, lfcSE, stat, pvalue, padj
+
+ℹ Use `spec()` to retrieve the full column specification for this data.
+ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
@@ -812,14 +3409,27 @@ Drop NA
values
Over-representation Analysis (ORA)
-To test for over-representation, we can calculate a p-value with a hypergeometric test (ref).
-\(p = 1 - \displaystyle\sum_{i = 0}^{k-1}\frac{ {M \choose i}{ {N-M} \choose {n-i} } } { {N \choose n} }\)
-Where N
is the number of genes in the background distribution, M
is the number of genes in a pathway, n
is the number of genes we are interested in (our marker genes), and k
is the number of genes that overlap between the pathway and our marker genes.
-Borrowing an example from clusterProfiler: universal enrichment tool for functional and comparative study (Yu ):
+To test for over-representation, we can calculate a p-value with a
+hypergeometric test (ref).
+\(p = 1 - \displaystyle\sum_{i =
+0}^{k-1}\frac{ {M \choose i}{ {N-M} \choose {n-i} } } { {N \choose n}
+}\)
+Where N
is the number of genes in the background
+distribution, M
is the number of genes in a pathway,
+n
is the number of genes we are interested in (our marker
+genes), and k
is the number of genes that overlap between
+the pathway and our marker genes.
+Borrowing an example from clusterProfiler:
+universal enrichment tool for functional and comparative study (Yu
+):
-Example: Suppose we have 17,980 genes detected in a Microarray study and 57 genes were differentially expressed. Among the differential expressed genes, 28 are annotated to a gene set.
+Example: Suppose we have 17,980 genes detected in a
+Microarray study and 57 genes were differentially expressed. Among the
+differential expressed genes, 28 are annotated to a gene set.
-We’ll call genes that are differentially expressed gene_in_interest
and genes that are in the gene set in_gene_set
.
+We’ll call genes that are differentially expressed
+gene_in_interest
and genes that are in the gene set
+in_gene_set
.
@@ -838,7 +3448,10 @@ Over-representation Analysis (ORA)
-We can assess if the 28 overlapping genes mean that the differentially expressed genes are over-represented in the gene set with the hypergeometric distribution. This corresponds to a one-sided Fisher’s exact test.
+We can assess if the 28 overlapping genes mean that the
+differentially expressed genes are over-represented in the gene set with
+the hypergeometric distribution. This corresponds to a one-sided
+Fisher’s exact test.
@@ -859,56 +3472,88 @@ Over-representation Analysis (ORA)
-When we test multiple pathways or gene sets, the p-values then need to be adjusted for multiple hypothesis testing.
+When we test multiple pathways or gene sets, the
+p-values then need to be adjusted for multiple
+hypothesis testing.
High stem cell capacity ORA
-Our DGE results are from data published as part of Sachs et al. (2014). The authors sorted populations of primary leukemia cells and examined the stem cell capacity of these cell populations. (This study may sound familiar if you’ve worked on one of our bulk RNA-seq exercise notebooks in the past!)
-We compared the population that the authors identified as having high stem cell capacity to a low stem cell capacity population. We also compared the high stem cell capacity cells to a mix of populations (e.g., unsorted cells). You can see the code in here.
-We’re interested in what pathways are over-represented in genes that specifically distinguish the high capacity population from the low capacity population.
-Let’s generate a list of genes that have higher expression in the high stem cell capacity population compared to the low stem cell capacity population, but we’ll also want to exclude genes that show up in our other comparison to unsorted cells.
-We’ll start with the high stem cell capacity vs. low stem cell capacity population comparison. Genes with positive log2 fold-changes (LFC) will be more highly expressed in the high stem cell capacity cells based on how we set up the analysis.
+Our DGE results are from data published as part of Sachs et
+al. (2014). The authors sorted populations of primary leukemia
+cells and examined the stem cell capacity of these cell populations.
+(This study may sound familiar if you’ve worked on one of our bulk
+RNA-seq exercise notebooks in the past!)
+We compared the population that the authors identified as having high
+stem cell capacity to a low stem cell capacity population. We also
+compared the high stem cell capacity cells to a mix of populations
+(e.g., unsorted cells). You can see the code in here.
+We’re interested in what pathways are over-represented in genes that
+specifically distinguish the high capacity population from the low
+capacity population.
+Let’s generate a list of genes that have higher expression in the
+high stem cell capacity population compared to the low stem cell
+capacity population, but we’ll also want to exclude genes that show up
+in our other comparison to unsorted cells.
+We’ll start with the high stem cell capacity vs. low stem cell
+capacity population comparison. Genes with positive log2 fold-changes
+(LFC) will be more highly expressed in the high stem cell capacity cells
+based on how we set up the analysis.
-
-vs_low_genes <- vs_low_df %>%
+
+vs_low_genes <- vs_low_df |>
# Filter to the positive LFC and filter based on significance too (padj)
dplyr::filter(log2FoldChange > 0,
- padj < 0.05) %>%
+ padj < 0.05) |>
# Return a vector of gene symbols
dplyr::pull(gene_symbol)
-Although we’re picking a commonly used cutoff (FDR < 0.05), it’s still arbitrary and we could just as easily pick a different threshold for our LFC values. When we generate lists of genes of interest for ORA, we typically pick an arbitrary cutoff. This is one of the approach’s weaknesses – we’ve removed all other context.
+Although we’re picking a commonly used cutoff (FDR <
+0.05), it’s still arbitrary and we could just as easily pick a different
+threshold for our LFC values. When we generate lists of genes of
+interest for ORA, we typically pick an arbitrary cutoff. This is one of
+the approach’s weaknesses – we’ve removed all other context.
Now, we’ll take the same steps for our other results.
-
-vs_unsorted_genes <- vs_unsorted_df %>%
+
+vs_unsorted_genes <- vs_unsorted_df |>
dplyr::filter(log2FoldChange > 0,
- padj < 0.05) %>%
+ padj < 0.05) |>
dplyr::pull(gene_symbol)
-We want genes that are in the first comparison but not in the second! We can use setdiff()
, a base R function for set operations, to get the list that we want.
+We want genes that are in the first comparison but not in the second!
+We can use setdiff()
, a base R function for set operations,
+to get the list that we want.
-
+
# What genes are in the first set but *not* in the second set
-genes_for_ora <- setdiff(vs_low_genes, vs_unsorted_genes)
+genes_for_ora <- setdiff(vs_low_genes, vs_unsorted_genes)
Background set
-As we saw above, calculating the p-value relies on the number of genes in the background distribution. Sometimes folks consider genes from the entire genome to comprise the background, but in the example borrowed from the clusterProfiler
authors, they state:
+As we saw above, calculating the p-value relies on the number of
+genes in the background distribution. Sometimes folks consider genes
+from the entire genome to comprise the background, but in the example
+borrowed from the clusterProfiler
authors, they state:
17,980 genes detected in a Microarray study
Where the key phrase is genes detected.
-If we were unable to include a gene in one of our differential expression comparisons because, for example, it had low mean expression in our experiment and therefore was filtered out in our tidyr::drop_na()
step, we shouldn’t include in our background set.
-We can use another function for set operations, intersect()
, to get our background set of genes that were included in both comparisons.
+If we were unable to include a gene in one of our differential
+expression comparisons because, for example, it had low mean expression
+in our experiment and therefore was filtered out in our
+tidyr::drop_na()
step, we shouldn’t include in our
+background set.
+We can use another function for set operations,
+intersect()
, to get our background set of genes that were
+included in both comparisons.
@@ -927,25 +3572,29 @@ Background set
Run enricher()
-Now that we have our background set, our genes of interest, and our pathway information, we’re ready to run ORA using the enricher()
function.
+Now that we have our background set, our genes of interest, and our
+pathway information, we’re ready to run ORA using the
+enricher()
function.
-
+
kegg_ora_results <- enricher(
gene = genes_for_ora, # Genes of interest
- pvalueCutoff = 0.05,
+ pvalueCutoff = 0.05,
pAdjustMethod = "BH", # FDR
universe = background_set, # Background set
- # The pathway information should be a data frame with a term name or
+ # The pathway information should be a data frame with a term name or
# identifier and the gene identifiers
- TERM2GENE = dplyr::select(mm_kegg_df,
+ TERM2GENE = dplyr::select(mm_kegg_df,
gs_name,
gene_symbol)
)
-Note: using enrichKEGG()
is a shortcut for doing ORA using KEGG, but the approach we covered here can be used with any gene sets you’d like!
+Note: using enrichKEGG()
is a shortcut for doing ORA
+using KEGG, but the approach we covered here can be used with any gene
+sets you’d like!
What is returned by enricher()
?
@@ -954,7 +3603,9 @@ Run enricher()
-The information we’re most likely interested in is in the results
slot. Let’s convert this into a data frame that we can write to file.
+The information we’re most likely interested in is in the
+results
slot. Let’s convert this into a data frame that we
+can write to file.
@@ -965,43 +3616,47 @@ Run enricher()
Visualizing results
-We can use a dot plot to visualize our significant enrichment results.
+We can use a dot plot to visualize our significant enrichment
+results.
enrichplot::dotplot(kegg_ora_results)
-
-wrong orderBy parameter; set to default `orderBy = "x"`
-
-
+
-We can use an UpSet plot to visualize the overlap between the gene sets that were returned as significant.
+We can use an UpSet
+plot to visualize the overlap between the gene sets
+that were returned as significant.
enrichplot::upsetplot(kegg_ora_results)
-
+
-We can see that some of the DNA repair pathways share genes. Gene sets or pathways aren’t independent, either! Sometimes multiple pathways that show up in our results as significant are indicative of only a handful of genes in our gene list.
-We can look at the geneID
column of our results to see what genes overlap; it’s a good idea to take a look.
+We can see that some of the DNA repair pathways share genes. Gene
+sets or pathways aren’t independent, either! Sometimes multiple pathways
+that show up in our results as significant are indicative of only a
+handful of genes in our gene list.
+We can look at the geneID
column of our results to see
+what genes overlap; it’s a good idea to take a look.
-
-kegg_result_df %>%
+
+kegg_result_df |>
# Use dplyr::select() - the name of the pathway is in the ID column
dplyr::select(ID, geneID)
@@ -1026,67 +3681,76 @@ Session Info
sessionInfo()
-
-R version 4.0.3 (2020-10-10)
-Platform: x86_64-pc-linux-gnu (64-bit)
-Running under: Ubuntu 20.04 LTS
+
+R version 4.4.1 (2024-06-14)
+Platform: x86_64-pc-linux-gnu
+Running under: Ubuntu 22.04.4 LTS
Matrix products: default
-BLAS/LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.8.so
+BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
+LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
- [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=C
+ [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
+time zone: Etc/UTC
+tzcode source: system (glibc)
+
attached base packages:
-[1] parallel stats4 stats graphics grDevices utils datasets
-[8] methods base
+[1] stats4 stats graphics grDevices utils datasets methods
+[8] base
other attached packages:
- [1] org.Mm.eg.db_3.12.0 AnnotationDbi_1.52.0 IRanges_2.24.1
- [4] S4Vectors_0.28.1 Biobase_2.50.0 BiocGenerics_0.36.0
- [7] msigdbr_7.2.1 clusterProfiler_3.18.1 magrittr_2.0.1
-[10] optparse_1.6.6
+[1] org.Mm.eg.db_3.19.1 AnnotationDbi_1.66.0 IRanges_2.38.0
+[4] S4Vectors_0.42.0 Biobase_2.64.0 BiocGenerics_0.50.0
+[7] msigdbr_7.5.1 clusterProfiler_4.12.0 optparse_1.7.5
loaded via a namespace (and not attached):
- [1] enrichplot_1.10.2 bit64_4.0.5 RColorBrewer_1.1-2
- [4] tools_4.0.3 R6_2.5.0 DBI_1.1.1
- [7] colorspace_2.0-0 tidyselect_1.1.0 gridExtra_2.3
-[10] bit_4.0.4 compiler_4.0.3 cli_2.2.0
-[13] scatterpie_0.1.5 labeling_0.4.2 shadowtext_0.0.7
-[16] scales_1.1.1 readr_1.4.0 stringr_1.4.0
-[19] digest_0.6.27 ggupset_0.3.0 rmarkdown_2.6
-[22] DOSE_3.16.0 pkgconfig_2.0.3 htmltools_0.5.1.1
-[25] fastmap_1.1.0 rlang_0.4.10 rstudioapi_0.13
-[28] RSQLite_2.2.3 farver_2.0.3 generics_0.1.0
-[31] jsonlite_1.7.2 BiocParallel_1.24.1 GOSemSim_2.16.1
-[34] dplyr_1.0.3 GO.db_3.12.1 Matrix_1.3-2
-[37] fansi_0.4.2 Rcpp_1.0.6 munsell_0.5.0
-[40] viridis_0.5.1 lifecycle_0.2.0 stringi_1.5.3
-[43] yaml_2.2.1 ggraph_2.0.4 MASS_7.3-53
-[46] plyr_1.8.6 qvalue_2.22.0 grid_4.0.3
-[49] blob_1.2.1 ggrepel_0.9.1 DO.db_2.9
-[52] crayon_1.3.4 lattice_0.20-41 graphlayouts_0.7.1
-[55] cowplot_1.1.1 splines_4.0.3 hms_1.0.0
-[58] ps_1.5.0 knitr_1.30 pillar_1.4.7
-[61] fgsea_1.16.0 igraph_1.2.6 reshape2_1.4.4
-[64] fastmatch_1.1-0 glue_1.4.2 evaluate_0.14
-[67] downloader_0.4 data.table_1.13.6 BiocManager_1.30.10
-[70] vctrs_0.3.6 tweenr_1.0.1 gtable_0.3.0
-[73] getopt_1.20.3 purrr_0.3.4 polyclip_1.10-0
-[76] tidyr_1.1.2 assertthat_0.2.1 cachem_1.0.1
-[79] ggplot2_3.3.3 xfun_0.20 ggforce_0.3.2
-[82] tidygraph_1.2.0 viridisLite_0.3.0 tibble_3.0.5
-[85] rvcheck_0.1.8 memoise_1.1.0 ellipsis_0.3.1
+ [1] DBI_1.2.2 gson_0.1.0 shadowtext_0.1.3
+ [4] gridExtra_2.3 rlang_1.1.3 magrittr_2.0.3
+ [7] DOSE_3.30.0 compiler_4.4.1 RSQLite_2.3.6
+ [10] png_0.1-8 vctrs_0.6.5 reshape2_1.4.4
+ [13] stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2
+ [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3
+ [19] ggraph_2.2.1 utf8_1.2.4 HDO.db_0.99.1
+ [22] rmarkdown_2.26 tzdb_0.4.0 enrichplot_1.24.0
+ [25] UCSC.utils_1.0.0 purrr_1.0.2 bit_4.0.5
+ [28] xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8
+ [31] aplot_0.2.2 GenomeInfoDb_1.40.0 jsonlite_1.8.8
+ [34] blob_1.2.4 highr_0.10 BiocParallel_1.38.0
+ [37] tweenr_2.0.3 parallel_4.4.1 R6_2.5.1
+ [40] bslib_0.7.0 stringi_1.8.3 RColorBrewer_1.1-3
+ [43] jquerylib_0.1.4 GOSemSim_2.30.0 Rcpp_1.0.12
+ [46] knitr_1.46 readr_2.1.5 Matrix_1.7-0
+ [49] splines_4.4.1 igraph_2.0.3 tidyselect_1.2.1
+ [52] qvalue_2.36.0 yaml_2.3.8 viridis_0.6.5
+ [55] codetools_0.2-20 lattice_0.22-6 tibble_3.2.1
+ [58] plyr_1.8.9 treeio_1.28.0 withr_3.0.0
+ [61] KEGGREST_1.44.0 evaluate_0.23 gridGraphics_0.5-1
+ [64] scatterpie_0.2.2 getopt_1.20.4 polyclip_1.10-6
+ [67] ggupset_0.3.0 Biostrings_2.72.0 ggtree_3.12.0
+ [70] pillar_1.9.0 ggfun_0.1.4 generics_0.1.3
+ [73] vroom_1.6.5 hms_1.1.3 ggplot2_3.5.1
+ [76] tidytree_0.4.6 munsell_0.5.1 scales_1.3.0
+ [79] glue_1.7.0 lazyeval_0.2.2 tools_4.4.1
+ [82] data.table_1.15.4 fgsea_1.30.0 babelgene_22.9
+ [85] fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4
+ [88] tidygraph_1.3.1 cowplot_1.1.3 grid_4.4.1
+ [91] ape_5.8 tidyr_1.3.1 colorspace_2.1-0
+ [94] nlme_3.1-164 patchwork_1.2.0 GenomeInfoDbData_1.2.12
+ [97] ggforce_0.4.2 cli_3.6.2 fansi_1.0.6
+[100] viridisLite_0.4.2
+ [ reached getOption("max.print") -- omitted 15 entries ]
-LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogT3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjAKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBQZXJmb3JtIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIHdpdGggW2BBbm5vdGF0aW9uREJJYCBhbm5vdGF0aW9uIHBhY2thZ2VzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9Bbm5vdGF0aW9uRGJpL2luc3QvZG9jL0ludHJvVG9Bbm5vdGF0aW9uUGFja2FnZXMucGRmKQotIEFjY2VzcyBbTW9sZWN1bGFyIFNpZ25hdHVyZXMgRGF0YWJhc2UgZ2VuZSBzZXQgY29sbGVjdGlvbnNdKGh0dHBzOi8vd3d3LmdzZWEtbXNpZ2RiLm9yZy9nc2VhL21zaWdkYi9jb2xsZWN0aW9ucy5qc3ApIHZpYSB0aGUgYG1zaWdkYnJgIHBhY2thZ2UKLSBQcmVwYXJlIGdlbmUgc2V0cyBmb3Igb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgaW5jbHVkaW5nIGFuIGFwcHJvcHJpYXRlIGJhY2tncm91bmQgc2V0Ci0gUGVyZm9ybSBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIHdpdGggdGhlIGBjbHVzdGVyUHJvZmlsZXJgIHBhY2thZ2UKCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2UnbGwgY292ZXIgYSB0eXBlIG9mIHBhdGh3YXkgb3IgZ2VuZSBzZXQgYW5hbHlzaXMgY2FsbGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgKE9SQSkuClRoZSBpZGVhIGJlaGluZCBPUkEgaXMgcmVsYXRpdmVseSBzdHJhaWdodGZvcndhcmQ6IGdpdmVuIGEgc2V0IG9mIGdlbmVzLCBkbyB0aGVzZSBnZW5lcyBvdmVybGFwIHdpdGggYSBwYXRod2F5IG1vcmUgdGhhbiB3ZSBleHBlY3QgYnkgY2hhbmNlPwpUaGUgc2ltcGxpY2l0eSBvZiBvbmx5IHJlcXVpcmluZyBhbiBpbnB1dCBnZW5lIHNldCAoc29ydCBvZiwgbW9yZSBvbiB0aGF0IGJlbG93KSBjYW4gYmUgYXR0cmFjdGl2ZS4KCk9SQSBoYXMgc29tZSBsaW1pdGF0aW9ucywgb3V0bGluZWQgbmljZWx5IChhbmQgbW9yZSBleHRlbnNpdmVseSEpIGluIFtLaGF0cmkgX2V0IGFsLl8gKDIwMTIpXSggaHR0cHM6Ly9kb2kub3JnLzEwLjEzNzEvam91cm5hbC5wY2JpLjEwMDIzNzUpLiAKT25lIG9mIHRoZSBtYWluIGlzc3VlcyB3aXRoIE9SQSBpcyB0aGF0IHR5cGljYWxseSBhbGwgZ2VuZXMgYXJlIHRyZWF0ZWQgYXMgZXF1YWwgLS0gdGhlIGNvbnRleHQgb2YgdGhlIG1hZ25pdHVkZSBvZiBhIGNoYW5nZSB3ZSBtYXkgYmUgbWVhc3VyaW5nIGlzIHJlbW92ZWQgYW5kIGVhY2ggZ2VuZSBpcyB0cmVhdGVkIGFzIGluZGVwZW5kZW50LCB3aGljaCBjYW4gc29tZXRpbWVzIHJlc3VsdCBpbiBhbiBpbmNvcnJlY3QgZXN0aW1hdGUgb2Ygc2lnbmlmaWNhbmNlLgoKV2Ugd2lsbCB1c2UgdGhlIFtgY2x1c3RlclByb2ZpbGVyYCBwYWNrYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvY2x1c3RlclByb2ZpbGVyLmh0bWwpIChbWXUgKmV0IGFsLiogMjAxMl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwODkvb21pLjIwMTEuMDExOC4pKSB0byBwZXJmb3JtIE9SQS4gCmBjbHVzdGVyUHJvZmlsZXJgIGhhcyBtYW55IGJ1aWx0LWluIGZ1bmN0aW9ucyB0aGF0IHdpbGwgcnVuIGEgc3BlY2lmaWMgdHlwZSBvZiBhbmFseXNpcyB1c2luZyBhIHNwZWNpZmljIHNvdXJjZSBvZiBwYXRod2F5cy9nZW5lIHNldHMgYXV0b21hdGljYWxseSwgYnV0IGZvciBvdXIgcHVycG9zZXMgd2UncmUgZ29pbmcgdG8ga2VlcCB0aGluZ3MgYXMgZ2VuZXJhbCBhcyBwb3NzaWJsZS4KU2VlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgYm9va10oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2luZGV4Lmh0bWwpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwYWNrYWdlJ3MgZnVsbCBzdWl0ZSBvZiBmdW5jdGlvbmFsaXR5LgoKQmVjYXVzZSBkaWZmZXJlbnQgYmlvaW5mb3JtYXRpY3MgdG9vbHMgb2Z0ZW4gcmVxdWlyZSBkaWZmZXJlbnQgdHlwZXMgb2YgZ2VuZSBpZGVudGlmaWVycywgd2UnbGwgYWxzbyBjb3ZlciBob3cgdG8gY29udmVydCBiZXR3ZWVuIGdlbmUgaWRlbnRpZmllcnMgdXNpbmcgW2BBbm5vdGF0aW9uRGJpYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0Fubm90YXRpb25EYmkuaHRtbCkgQmlvY29uZHVjdG9yIHBhY2thZ2VzIGluIHRoaXMgbm90ZWJvb2suCkNoZWNrIG91dCB0aGUgW19Bbm5vdGF0aW9uRGJpOiBJbnRyb2R1Y3Rpb24gVG8gQmlvY29uZHVjdG9yIEFubm90YXRpb24gUGFja2FnZXNfIChDYXJsc29uIDIwMjAuKSB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvQW5ub3RhdGlvbkRiaS9pbnN0L2RvYy9JbnRyb1RvQW5ub3RhdGlvblBhY2thZ2VzLnBkZikgZm9yIG1vcmUgaW5mb3JtYXRpb24uCgojIyMjIE90aGVyIHJlc291cmNlcwoKKiBGb3IgYW5vdGhlciBleGFtcGxlIHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgLCBzZWUgW19JbnRybyB0byBER0U6IEZ1bmN0aW9uYWwgQW5hbHlzaXMuXyBmcm9tIEhhcnZhcmQgQ2hhbiBCaW9pbmZvcm1hdGljcyBDb3JlIFRyYWluaW5nLl0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDlfZnVuY3Rpb25hbF9hbmFseXNpcy5odG1sKQoqIFtgV2ViR2VzdGFsdFJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvV2ViR2VzdGFsdFIvKSBpcyBhbm90aGVyIFIgcGFja2FnZSB0aGF0IGNhbiBiZSB1c2VkIGZvciBPUkEuCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMKCmBgYHtyIGxpYnJhcmllc30KIyBQaXBlcwpsaWJyYXJ5KG1hZ3JpdHRyKQojIFBhY2thZ2Ugd2UnbGwgdXNlIHRvIApsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKIyBQYWNrYWdlIHRoYXQgY29udGFpbnMgTVNpZ0RCIGdlbmUgc2V0cyBpbiB0aWR5IGZvcm1hdApsaWJyYXJ5KG1zaWdkYnIpCiMgTXVzIG11c2N1bHVzIGFubm90YXRpb24gcGFja2FnZSB3ZSdsbCB1c2UgZm9yIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uCmxpYnJhcnkob3JnLk1tLmVnLmRiKQpgYGAKCiMjIyBEaXJlY3RvcmllcyBhbmQgZmlsZXMKCiMjIyMgRGlyZWN0b3JpZXMKCmBgYHtyIGNyZWF0ZV9vcmFfZGlyZWN0b3J5LCBsaXZlID0gVFJVRX0KIyBXZSdsbCBjcmVhdGUgYSBkaXJlY3RvcnkgdG8gc3BlY2lmaWNhbGx5IGhvbGQgdGhlIE9SQSByZXN1bHRzIGlmIGl0IGRvZXNuJ3QKIyBleGlzdCB5ZXQKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgImxldWtlbWlhIikKaWYgKCFkaXIuZXhpc3RzKHJlc3VsdHNfZGlyKSkgewogIGRpci5jcmVhdGUocmVzdWx0c19kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgojIyMjIElucHV0IGZpbGVzCgpGb3Igb3VyIE9SQSBleGFtcGxlLCB3ZSdyZSBnb2luZyB0byB1c2UgdHdvIHRhYmxlcyBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIChER0UpIGFuYWx5c2lzIHJlc3VsdHMuCgpgYGB7ciBpbnB1dF9kaXJlY3Rvcnl9CmlucHV0X2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAibGV1a2VtaWEiKQoKIyBUaGlzIGZpbGUgY29udGFpbnMgdGhlIERHRSByZXN1bHRzIGZvciBhIGNlbGwgcG9wdWxhdGlvbiB3aXRoIGhpZ2ggc3RlbSBjZWxsCiMgY2FwYWNpdHkgYXMgY29tcGFyZWQgdG8gYSBjZWxsIHBvcHVsYXRpb24gCnZzX2xvd19maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hfY2FwYWNpdHlfdnNfbG93X2NhcGFjaXR5X3Jlc3VsdHMudHN2IikKCiMgVGhpcyBmaWxlIGNvbnRhaW5zIHRoZSBER0UgcmVzdWx0cyBmb3IgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24KIyB2cy4gdW5zb3J0ZWQgY2VsbHMgKGUuZy4sIGEgbWl4dHVyZSBvZiBjYXBhY2l0aWVzKQp2c191bnNvcnRlZF9maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoX2NhcGFjaXR5X3ZzX3Vuc29ydGVkX3Jlc3VsdHMudHN2IikKYGBgCgojIyMjIE91dHB1dCBmaWxlcwoKV2UnbGwgc2F2ZSB0aGUgdGFibGUgb2YgT1JBIHJlc3VsdHMgKGUuZy4sIHAtdmFsdWVzKS4KCmBgYHtyIG91dHB1dF9maWxlLCBsaXZlID0gVFJVRX0Ka2VnZ19yZXN1bHRzX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAibGV1a2VtaWFfa2VnZ19vcmFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgc2V0cwoKV2Ugd2lsbCB1c2UgZ2VuZSBzZXRzIGZyb20gdGhlIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKV0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2luZGV4LmpzcCkgZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlIChbU3VicmFtYW5pYW4sIFRhbWF5byAqZXQgYWwuKiAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpKS4gClRoZSBbYG1zaWdkYnJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbXNpZ2Rici9pbmRleC5odG1sKSBwYWNrYWdlIGNvbnRhaW5zIE1TaWdEQiBkYXRhc2V0cyBhbHJlYWR5IGluIHRoZSB0aWR5IGZvcm1hdCByZXF1aXJlZCBieSBgY2x1c3RlclByb2ZpbGVyYCBhbmQgc3VwcG9ydHMgbXVsdGlwbGUgb3JnYW5pc21zLgoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgd2hhdCBvcmdhbmlzbXMgdGhlIHBhY2thZ2Ugc3VwcG9ydHMuCgpgYGB7ciBzaG93X3NwZWNpZXN9Cm1zaWdkYnJfc3BlY2llcygpCmBgYAoKVGhlIHJlc3VsdHMgd2UncmUgaW50ZXJlc3RlZCBpbiBoZXJlIGNvbWUgZnJvbSBtb3VzZSBzYW1wbGVzLCBzbyB3ZSBjYW4gb2J0YWluIGp1c3QgdGhlIGdlbmUgc2V0cyByZWxldmFudCB0byBfTS4gbXVzY3VsdXNfIHdpdGggdGhlIGBzcGVjaWVzYCBhcmd1bWVudCB0byBgbXNpZ2RicigpYC4KCmBgYHtyIG1tX2RmLCBsaXZlID0gVFJVRX0KbW1fbXNpZ2RiX2RmIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJNdXMgbXVzY3VsdXMiKQpgYGAKCk1TaWdEQiBjb250YWlucyA4IGRpZmZlcmVudCBnZW5lIHNldCBjb2xsZWN0aW9ucy4KCiAgICBIOiBoYWxsbWFyayBnZW5lIHNldHMKICAgIEMxOiBwb3NpdGlvbmFsIGdlbmUgc2V0cwogICAgQzI6IGN1cmF0ZWQgZ2VuZSBzZXRzCiAgICBDMzogbW90aWYgZ2VuZSBzZXRzCiAgICBDNDogY29tcHV0YXRpb25hbCBnZW5lIHNldHMKICAgIEM1OiBHTyBnZW5lIHNldHMKICAgIEM2OiBvbmNvZ2VuaWMgc2lnbmF0dXJlcwogICAgQzc6IGltbXVub2xvZ2ljIHNpZ25hdHVyZXMKCkluIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgY2Fub25pY2FsIHBhdGh3YXlzIHdoaWNoIGFyZSAoW3JlZl0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25zLmpzcCkpOgoKPiBHZW5lIHNldHMgZnJvbSBwYXRod2F5IGRhdGFiYXNlcy4gVXN1YWxseSwgdGhlc2UgZ2VuZSBzZXRzIGFyZSBjYW5vbmljYWwgcmVwcmVzZW50YXRpb25zIG9mIGEgYmlvbG9naWNhbCBwcm9jZXNzIGNvbXBpbGVkIGJ5IGRvbWFpbiBleHBlcnRzLgoKQW5kIGFyZSBhIHN1YnNldCBvZiBgQzI6IGN1cmF0ZWQgZ2VuZSBzZXRzYC4KU3BlY2lmaWNhbGx5LCB3ZSB3aWxsIHVzZSB0aGUgW0tFR0cgKEt5b3RvIEVuY3ljbG9wZWRpYSBvZiBHZW5lcyBhbmQgR2Vub21lcyldKGh0dHBzOi8vd3d3Lmdlbm9tZS5qcC9rZWdnLykgcGF0aHdheXMuIAoKYGBge3IgZmlsdGVyX3RvX2tlZ2d9CiMgRmlsdGVyIHRoZSBtb3VzZSBkYXRhIGZyYW1lIHRvIHRoZSBLRUdHIHBhdGh3YXlzIHRoYXQgYXJlIGluY2x1ZGVkIGluIHRoZQojIGN1cmF0ZWQgZ2VuZSBzZXRzCm1tX2tlZ2dfZGYgPC0gbW1fbXNpZ2RiX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfY2F0ID09ICJDMiIsICAjIGN1cmF0ZWQgZ2VuZSBzZXRzIAogICAgICAgICAgICAgICAgZ3Nfc3ViY2F0ID09ICJDUDpLRUdHIikgICMgS0VHRyBwYXRod2F5cyAKYGBgCgoqTm90ZTogV2UgY291bGQgc3BlY2lmaWVkIHRoYXQgd2Ugd2FudGVkIHRoZSBLRUdHIGdlbmUgc2V0cyB1c2luZyB0aGUgYGNhdGVnb3J5YCBhbmQgYHN1YmNhdGVnb3J5YCBhcmd1bWVudHMgb2YgYG1zaWdkYnIoKWAsIGJ1dCB3ZSdyZSBnb2luZyBmb3IgZ2VuZXJhbCBzdGVwcyEqCgpgYGB7ciBtbV9rZWdnX2NvbHVtbnN9CmNvbG5hbWVzKG1tX2tlZ2dfZGYpCmBgYAoKVGhlIGBjbHVzdGVyUHJvZmlsZXJgIGZ1bmN0aW9uIHdlIHdpbGwgdXNlIHJlcXVpcmVzIGEgZGF0YSBmcmFtZSB3aXRoIHR3byBjb2x1bW5zLCB3aGVyZSBvbmUgY29sdW1uIGNvbnRhaW5zIHRoZSB0ZXJtIGlkZW50aWZpZXIgb3IgbmFtZSBhbmQgb25lIGNvbHVtbiBjb250YWlucyBnZW5lIGlkZW50aWZpZXJzIHRoYXQgbWF0Y2ggb3VyIGdlbmUgbGlzdHMgd2Ugd2FudCB0byBjaGVjayBmb3IgZW5yaWNobWVudC4KT3VyIGRhdGEgZnJhbWUgd2l0aCBLRUdHIHRlcm1zIGNvbnRhaW5zIEVudHJleiBJRHMgYW5kIGdlbmUgc3ltYm9scy4KCiMjIFJlYWQgaW4gREdFIHJlc3VsdHMgYW5kIHByZXAKCmBgYHtyIHJlYWRfaW5fZGdlX3Jlc3VsdHMsIGxpdmUgPSBUUlVFfQp2c19sb3dfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZzX2xvd19maWxlKQpgYGAKCkxldCdzIHRha2UgYSBwZWVrIGF0IHRoZSB0b3Agb2YgdGhlIERHRSByZXN1bHRzIGRhdGEgZnJhbWUuCgpgYGB7ciBwZWVrX2F0X2RnZV9yZXN1bHRzLCBsaXZlID0gVFJVRX0KaGVhZCh2c19sb3dfZGYpCmBgYAoKIyMjIEdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uCgpPdXIgZGF0YSBmcmFtZSBvZiBER0UgcmVzdWx0cyBjb250YWlucyBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMuClNvIHdlIHdpbGwgbmVlZCB0byBjb252ZXJ0IGZyb20gdGhlc2UgaWRlbnRpZmllcnMgaW50byBlaXRoZXIgdGhlIGdlbmUgc3ltYm9scyBvciBFbnRyZXogSURzIHRoYXQgYXJlIHByZXNlbnQgaW4gdGhlIGRhdGEgd2UgZXh0cmFjdGVkIHdpdGggYG1zaWdkYnIoKWAuCgpXZSdyZSBnb2luZyB0byBjb252ZXJ0IG91ciBpZGVudGlmaWVycyB0byBnZW5lIHN5bWJvbHMgYmVjYXVzZSB0aGV5IGFyZSBhIGJpdCBtb3JlIGh1bWFuIHJlYWRhYmxlLCBidXQgeW91IGNhbiwgd2l0aCB0aGUgY2hhbmdlIG9mIGEgc2luZ2xlIGFyZ3VtZW50LCB1c2UgdGhlIHNhbWUgY29kZSB0byBjb252ZXJ0IHRvIG1hbnkgb3RoZXIgdHlwZXMgb2YgaWRlbnRpZmllcnMhCgpUaGUgYW5ub3RhdGlvbiBwYWNrYWdlIGBvcmcuTW0uZWcuZGJgIGNvbnRhaW5zIGluZm9ybWF0aW9uIGZvciBkaWZmZXJlbnQgaWRlbnRpZmllcnMuCmBvcmcuTW0uZWcuZGJgIGlzIHNwZWNpZmljIHRvIF9NdXMgbXVzY3VsdXNfIC0tIHRoaXMgaXMgd2hhdCB0aGUgYE1tYCBpbiB0aGUgcGFja2FnZSBuYW1lIGlzIHJlZmVyZW5jaW5nLgpUbyBwZXJmb3JtIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIGluIGh1bWFuIChfSG9tbyBzYXBpZW5zXykgd2UgY291bGQgdXNlIGBvcmcuSHMuZWcuZGJgOwp3ZSB3b3VsZCB1c2UgYG9yZy5Eci5lZy5kYmAgZm9yIHplYnJhZmlzaCAoX0RhbmlvIHJlcmlvXykuCgpXZSBjYW4gc2VlIHdoYXQgdHlwZXMgb2YgSURzIGFyZSBhdmFpbGFibGUgdG8gdXMgaW4gYW4gYW5ub3RhdGlvbiBwYWNrYWdlIHdpdGggYGtleXR5cGVzKClgLgoKYGBge3Iga2V5dHlwZXMsIGxpdmUgPSBUUlVFfQprZXl0eXBlcyhvcmcuTW0uZWcuZGIpCmBgYAoKRXZlbiB0aG91Z2ggd2UnbGwgdXNlIHRoaXMgcGFja2FnZSB0byBjb252ZXJ0IGZyb20gRW5zZW1ibCBnZW5lIElEcyAoYEVOU0VNQkxgKSB0byBnZW5lIHN5bWJvbHMgKGBTWU1CT0xgKSwgd2UgY291bGQganVzdCBhcyBlYXNpbHkgdXNlIGl0IHRvIGNvbnZlcnQgZnJvbSBhbiBFbnNlbWJsIHRyYW5zY3JpcHQgSUQgKGBFTlNFTUJMVFJBTlNgKSB0byBFbnRyZXogSURzIChgRU5UUkVaSURgKS4KClRoZSBmdW5jdGlvbiB3ZSB3aWxsIHVzZSB0byBtYXAgZnJvbSBFbnNlbWJsIGdlbmUgSURzIHRvIGdlbmUgc3ltYm9scyBpcyBjYWxsZWQgYG1hcElkcygpYC4KCmBgYHtyIG1hcF90b19zeW1ib2x9CiMgVGhpcyByZXR1cm5zIGEgbmFtZWQgdmVjdG9yIHdoaWNoIHdlIGNhbiBjb252ZXJ0IHRvIGEgZGF0YSBmcmFtZSwgd2hlcmUKIyB0aGUga2V5cyAoRW5zZW1ibCBJRHMpIGFyZSB0aGUgbmFtZXMKc3ltYm9sc192ZWN0b3IgPC0gbWFwSWRzKG9yZy5NbS5lZy5kYiwgICMgU3BlY2lmeSB0aGUgYW5ub3RhdGlvbiBwYWNrYWdlCiAgICAgICAgICAgICAgICAgICAgICAgICAjIFRoZSB2ZWN0b3Igb2YgZ2VuZSBpZGVudGlmaWVycyB3ZSB3YW50IHRvIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBtYXAKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXMgPSB2c19sb3dfZGYkR2VuZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAjIFdoYXQgdHlwZSBvZiBnZW5lIGlkZW50aWZpZXJzIHdlJ3JlIHN0YXJ0aW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAjIHdpdGgKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXR5cGUgPSAiRU5TRU1CTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdHlwZSBvZiBnZW5lIGlkZW50aWZpZXIgd2Ugd2FudCByZXR1cm5lZAogICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uID0gIlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIyBJbiB0aGUgY2FzZSBvZiAxOm1hbnkgbWFwcGluZ3MsIHJldHVybiB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICMgZmlyc3Qgb25lLiBUaGlzIGlzIGRlZmF1bHQgYmVoYXZpb3IhCiAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aVZhbHMgPSAiZmlyc3QiKSAKCiMgV2Ugd291bGQgbGlrZSBhIGRhdGEgZnJhbWUgd2UgY2FuIGpvaW4gdG8gdGhlIERHRSByZXN1bHRzCnN5bWJvbHNfZGYgPC0gZGF0YS5mcmFtZSgKICBlbnNlbWJsX2lkID0gbmFtZXMoc3ltYm9sc192ZWN0b3IpLAogIGdlbmVfc3ltYm9sID0gc3ltYm9sc192ZWN0b3IKKQpgYGAKClRoaXMgbWVzc2FnZSBpcyBsZXR0aW5nIHVzIGtub3cgdGhhdCBzb21ldGltZXMgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzIHdpbGwgbWFwIHRvIG11bHRpcGxlIGdlbmUgc3ltYm9scy4KSW4gdGhpcyBjYXNlLCBpdCdzIGFsc28gcG9zc2libGUgdGhhdCBhIGdlbmUgc3ltYm9sIHdpbGwgbWFwIHRvIG11bHRpcGxlIEVuc2VtYmwgSURzLgoKTm93IHdlIGFyZSByZWFkeSB0byBhZGQgdGhlIGdlbmUgc3ltYm9scyB0byBvdXIgZGF0YSBmcmFtZSB3aXRoIHRoZSBER0UgcmVzdWx0cy4KV2UgY2FuIHVzZSBhIF9qb2luXyBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgdG8gZG8gdGhpcywgd2hpY2ggd2lsbCB1c2UgdGhlIEVuc2VtYmwgZ2VuZSBJRHMgaW4gYm90aCBkYXRhIGZyYW1lcyB0byBkZXRlcm1pbmUgaG93IHRvIGpvaW4gdGhlIHJvd3MuCgpMZXQncyBkbyB0aGlzIGZpcnN0IGZvciB0aGUgY29tcGFyaXNvbiB0byB0aGUgbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uLgoKYGBge3IgYWRkX3N5bWJvbHMsIGxpdmUgPSBUUlVFfQp2c19sb3dfZGYgPC0gc3ltYm9sc19kZiAlPiUKICAjIEFuICppbm5lciogam9pbiB3aWxsIG9ubHkgcmV0dXJuIHJvd3MgdGhhdCBhcmUgaW4gYm90aCBkYXRhIGZyYW1lcwogIGRwbHlyOjppbm5lcl9qb2luKHZzX2xvd19kZiwKICAgICAgICAgICAgICAgICAgICAjIFRoZSBuYW1lIG9mIHRoZSBjb2x1bW4gdGhhdCBjb250YWlucyB0aGUgRW5zZW1ibCBnZW5lIElEcwogICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIGxlZnQgZGF0YSBmcmFtZSBhbmQgcmlnaHQgZGF0YSBmcmFtZQogICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiZW5zZW1ibF9pZCIgPSAiR2VuZSIpKQpgYGAKCiMjIyBEcm9wIGBOQWAgdmFsdWVzCgpTb21lIG9mIHRoZXNlIHJvd3MgaGF2ZSBgTkFgIHZhbHVlcyBpbiBgcGFkamAsIHdoaWNoIFtjYW4gaGFwcGVuIGZvciBhIG51bWJlciBvZiByZWFzb25zXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjcHZhbHVlc05BKSBpbmNsdWRpbmcgd2hlbiBhbGwgc2FtcGxlcyBoYXZlIHplcm8gY291bnRzIG9yIGEgZ2VuZSBoYXMgbG93IG1lYW4gZXhwcmVzc2lvbi4KCkxldCdzIGZpbHRlciB0byByb3dzIHRoYXQgX2RvIG5vdCBoYXZlIGFueSBgTkFgXyB1c2luZyBhIGZ1bmN0aW9uIGB0aWR5cjo6ZHJvcF9uYSgpYC4KVGhpcyB3aWxsIGFsc28gZHJvcCBnZW5lcyB0aGF0IGhhdmUgYW4gRW5zZW1ibCBnZW5lIGlkZW50aWZpZXIgYnV0IG5vIGdlbmUgc3ltYm9sIQoKYGBge3IgY29tcGxldGVfY2FzZXN9CiMgUmVtb3ZlIHJvd3MgdGhhdCBhcmUgbm90IGNvbXBsZXRlIChlLmcuLCBjb250YWluIE5BcykgYnkgZmlsdGVyaW5nIHRvIG9ubHkgCiMgY29tcGxldGUgcm93cwp2c19sb3dfZGYgPC0gdnNfbG93X2RmICU+JQogIHRpZHlyOjpkcm9wX25hKCkKYGBgCgoqKk5vdyB3ZSdsbCByZWFkIGluIG91ciBkYXRhIGZyYW1lIG9mIERHRSByZXN1bHRzIGZyb20gYW5vdGhlciBjb21wYXJpc29uLioqClRvIHNhdmUgdXMgc29tZSB0aW1lIGR1cmluZyBpbnN0cnVjdGlvbiwgd2UndmUgYWxyZWFkeSBkb25lIHRoZSBnZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbiBhbmQgZmlsdGVyaW5nIHRvIHJlbW92ZSBgTkFgIHZhbHVlcyBpbiBbdGhpcyBub3RlYm9va10oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvdHJhaW5pbmctbW9kdWxlcy90cmVlL21hc3Rlci9wYXRod2F5LWFuYWx5c2lzL3NldHVwLzAxLWxldWtlbWlhX0RHRS5SbWQpLiAKV2UgdG9vayBhIGRpZmZlcmVudCBzZXJpZXMgb2Ygc3RlcHMgdG8gYWNoaWV2ZSB0aGUgc2FtZSB0aGluZywgd2hpY2ggaXMgb2Z0ZW4gcG9zc2libGUgaW4gUiEKCgpgYGB7ciByZWFkX2luX3Vuc29ydGVkfQp2c191bnNvcnRlZF9kZiA8LSByZWFkcjo6cmVhZF90c3YodnNfdW5zb3J0ZWRfZmlsZSkKYGBgCgojIyBPdmVyLXJlcHJlc2VudGF0aW9uIEFuYWx5c2lzIChPUkEpCgpUbyB0ZXN0IGZvciBvdmVyLXJlcHJlc2VudGF0aW9uLCB3ZSBjYW4gY2FsY3VsYXRlIGEgcC12YWx1ZSB3aXRoIGEgaHlwZXJnZW9tZXRyaWMgdGVzdCAoW3JlZl0oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2NoYXB0ZXIyLmh0bWwjb3Zlci1yZXByZXNlbnRhdGlvbi1hbmFseXNpcykpLgoKXChwID0gMSAtIFxkaXNwbGF5c3R5bGVcc3VtX3tpID0gMH1ee2stMX1cZnJhY3sge00gXGNob29zZSBpfXsge04tTX0gXGNob29zZSB7bi1pfSB9IH0geyB7TiBcY2hvb3NlIG59IH1cKQoKV2hlcmUgYE5gIGlzIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGJhY2tncm91bmQgZGlzdHJpYnV0aW9uLCBgTWAgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBpbiBhIHBhdGh3YXksIGBuYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHdlIGFyZSBpbnRlcmVzdGVkIGluIChvdXIgbWFya2VyIGdlbmVzKSwgYW5kIGBrYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHRoYXQgb3ZlcmxhcCBiZXR3ZWVuIHRoZSBwYXRod2F5IGFuZCBvdXIgbWFya2VyIGdlbmVzLgoKQm9ycm93aW5nIGFuIGV4YW1wbGUgZnJvbSBbX2NsdXN0ZXJQcm9maWxlcjogdW5pdmVyc2FsIGVucmljaG1lbnQgdG9vbCBmb3IgZnVuY3Rpb25hbCBhbmQgY29tcGFyYXRpdmUgc3R1ZHlfIChZdSApXShodHRwOi8veXVsYWItc211LnRvcC9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI292ZXItcmVwcmVzZW50YXRpb24tYW5hbHlzaXMpOgoKPiAqKkV4YW1wbGUqKjogU3VwcG9zZSB3ZSBoYXZlIDE3LDk4MCBnZW5lcyBkZXRlY3RlZCBpbiBhIE1pY3JvYXJyYXkgc3R1ZHkgYW5kIDU3IGdlbmVzIHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBBbW9uZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3NlZCBnZW5lcywgMjggYXJlIGFubm90YXRlZCB0byBhIGdlbmUgc2V0LgoKV2UnbGwgY2FsbCBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYGdlbmVfaW5faW50ZXJlc3RgIGFuZCBnZW5lcyB0aGF0IGFyZSBpbiB0aGUgZ2VuZSBzZXQgYGluX2dlbmVfc2V0YC4KCmBgYHtyIGdlbmVfdGFibGV9CmdlbmVfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBnZW5lX25vdF9pbnRlcmVzdCA9IGMoMjYxMywgMTUzMTApLAogIGdlbmVfaW5faW50ZXJlc3QgPSBjKDI4LCAyOSkKKQpyb3duYW1lcyhnZW5lX3RhYmxlKSA8LSBjKCJpbl9nZW5lX3NldCIsICJub3RfaW5fZ2VuZV9zZXQiKQoKZ2VuZV90YWJsZQpgYGAKCldlIGNhbiBhc3Nlc3MgaWYgdGhlIDI4IG92ZXJsYXBwaW5nIGdlbmVzIG1lYW4gdGhhdCB0aGUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGFyZSBvdmVyLXJlcHJlc2VudGVkIGluIHRoZSBnZW5lIHNldCB3aXRoIHRoZSBoeXBlcmdlb21ldHJpYyBkaXN0cmlidXRpb24uClRoaXMgY29ycmVzcG9uZHMgdG8gYSBvbmUtc2lkZWQgRmlzaGVyJ3MgZXhhY3QgdGVzdC4KCmBgYHtyIGZpc2hlcl90ZXN0fQpmaXNoZXIudGVzdChnZW5lX3RhYmxlLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikKYGBgCgpXaGVuIHdlIHRlc3QgKiptdWx0aXBsZSBwYXRod2F5cyBvciBnZW5lIHNldHMqKiwgdGhlIHAtdmFsdWVzIHRoZW4gbmVlZCB0byBiZSAqKmFkanVzdGVkKiogZm9yIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZy4KCiMjIyBIaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSBPUkEKCk91ciBER0UgcmVzdWx0cyBhcmUgZnJvbSBkYXRhIHB1Ymxpc2hlZCBhcyBwYXJ0IG9mIFtTYWNocyBfZXQgYWwuXyAoMjAxNCldKGh0dHBzOi8vZHguZG9pLm9yZy8xMC4xMTgyJTJGYmxvb2QtMjAxMy0wOC01MjE3MDgpLgpUaGUgYXV0aG9ycyBzb3J0ZWQgcG9wdWxhdGlvbnMgb2YgcHJpbWFyeSBsZXVrZW1pYSBjZWxscyBhbmQgZXhhbWluZWQgdGhlIHN0ZW0gY2VsbCBjYXBhY2l0eSBvZiB0aGVzZSBjZWxsIHBvcHVsYXRpb25zLgooVGhpcyBzdHVkeSBtYXkgc291bmQgZmFtaWxpYXIgaWYgeW91J3ZlIHdvcmtlZCBvbiBvbmUgb2Ygb3VyIGJ1bGsgUk5BLXNlcSBleGVyY2lzZSBub3RlYm9va3MgaW4gdGhlIHBhc3QhKQoKV2UgY29tcGFyZWQgdGhlIHBvcHVsYXRpb24gdGhhdCB0aGUgYXV0aG9ycyBpZGVudGlmaWVkIGFzIGhhdmluZyBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSB0byBhIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbi4KV2UgYWxzbyBjb21wYXJlZCB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgY2VsbHMgdG8gYSBtaXggb2YgcG9wdWxhdGlvbnMgKGUuZy4sIHVuc29ydGVkIGNlbGxzKS4KWW91IGNhbiBzZWUgdGhlIGNvZGUgaW4gW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLW1vZHVsZXMvdHJlZS9tYXN0ZXIvcGF0aHdheS1hbmFseXNpcy9zZXR1cC8wMS1sZXVrZW1pYV9ER0UuUm1kKS4KCldlJ3JlIGludGVyZXN0ZWQgaW4gd2hhdCBwYXRod2F5cyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiBnZW5lcyB0aGF0IHNwZWNpZmljYWxseSBkaXN0aW5ndWlzaCB0aGUgaGlnaCBjYXBhY2l0eSBwb3B1bGF0aW9uIGZyb20gdGhlIGxvdyBjYXBhY2l0eSBwb3B1bGF0aW9uLgoKTGV0J3MgZ2VuZXJhdGUgYSBsaXN0IG9mIGdlbmVzIHRoYXQgaGF2ZSBoaWdoZXIgZXhwcmVzc2lvbiBpbiB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiBjb21wYXJlZCB0byB0aGUgbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uLCBidXQgd2UnbGwgYWxzbyB3YW50IHRvIGV4Y2x1ZGUgZ2VuZXMgdGhhdCBzaG93IHVwIGluIG91ciBfb3RoZXIgY29tcGFyaXNvbl8gdG8gdW5zb3J0ZWQgY2VsbHMuCgpXZSdsbCBzdGFydCB3aXRoIHRoZSBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSB2cy4gbG93IHN0ZW0gY2VsbCBjYXBhY2l0eSBwb3B1bGF0aW9uIGNvbXBhcmlzb24uCkdlbmVzIHdpdGggcG9zaXRpdmUgbG9nMiBmb2xkLWNoYW5nZXMgKExGQykgd2lsbCBiZSBtb3JlIGhpZ2hseSBleHByZXNzZWQgaW4gdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IGNlbGxzIGJhc2VkIG9uIGhvdyB3ZSBzZXQgdXAgdGhlIGFuYWx5c2lzLgoKYGBge3IgaGlnaF9jYXBhY2l0eV9nZW5lc30KdnNfbG93X2dlbmVzIDwtIHZzX2xvd19kZiAlPiUKICAjIEZpbHRlciB0byB0aGUgcG9zaXRpdmUgTEZDIGFuZCBmaWx0ZXIgYmFzZWQgb24gc2lnbmlmaWNhbmNlIHRvbyAocGFkaikKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSAlPiUKICAjIFJldHVybiBhIHZlY3RvciBvZiBnZW5lIHN5bWJvbHMKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkKYGBgCgpBbHRob3VnaCB3ZSdyZSBwaWNraW5nIGEgX2NvbW1vbmx5IHVzZWRfIGN1dG9mZiAoRkRSIDwgMC4wNSksIGl0J3Mgc3RpbGwgYXJiaXRyYXJ5IGFuZCB3ZSBjb3VsZCBqdXN0IGFzIGVhc2lseSBwaWNrIGEgZGlmZmVyZW50IHRocmVzaG9sZCBmb3Igb3VyIExGQyB2YWx1ZXMuCldoZW4gd2UgZ2VuZXJhdGUgbGlzdHMgb2YgZ2VuZXMgb2YgaW50ZXJlc3QgZm9yIE9SQSwgd2UgdHlwaWNhbGx5IHBpY2sgYW4gYXJiaXRyYXJ5IGN1dG9mZi4KVGhpcyBpcyBvbmUgb2YgdGhlIGFwcHJvYWNoJ3Mgd2Vha25lc3NlcyAtLSB3ZSd2ZSByZW1vdmVkIGFsbCBvdGhlciBjb250ZXh0LgoKTm93LCB3ZSdsbCB0YWtlIHRoZSBzYW1lIHN0ZXBzIGZvciBvdXIgb3RoZXIgcmVzdWx0cy4gCgpgYGB7ciB1bnNvcnRlZF9nZW5lc190b19yZW1vdmUsIGxpdmUgPSBUUlVFfQp2c191bnNvcnRlZF9nZW5lcyA8LSB2c191bnNvcnRlZF9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkKYGBgCgpXZSB3YW50IGdlbmVzIHRoYXQgYXJlIGluIHRoZSBmaXJzdCBjb21wYXJpc29uIGJ1dCBub3QgaW4gdGhlIHNlY29uZCEKV2UgY2FuIHVzZSBgc2V0ZGlmZigpYCwgYSBiYXNlIFIgZnVuY3Rpb24gZm9yIHNldCBvcGVyYXRpb25zLCB0byBnZXQgdGhlIGxpc3QgdGhhdCB3ZSB3YW50LgoKYGBge3Igc2V0ZGlmZn0KIyBXaGF0IGdlbmVzIGFyZSBpbiB0aGUgZmlyc3Qgc2V0IGJ1dCAqbm90KiBpbiB0aGUgc2Vjb25kIHNldApnZW5lc19mb3Jfb3JhIDwtIHNldGRpZmYodnNfbG93X2dlbmVzLCB2c191bnNvcnRlZF9nZW5lcykgCmBgYAoKIyMjIyBCYWNrZ3JvdW5kIHNldAoKQXMgd2Ugc2F3IGFib3ZlLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSByZWxpZXMgb24gdGhlIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgYmFja2dyb3VuZCBkaXN0cmlidXRpb24uClNvbWV0aW1lcyBmb2xrcyBjb25zaWRlciBnZW5lcyBmcm9tIHRoZSBlbnRpcmUgZ2Vub21lIHRvIGNvbXByaXNlIHRoZSBiYWNrZ3JvdW5kLCBidXQgaW4gdGhlIGV4YW1wbGUgYm9ycm93ZWQgZnJvbSB0aGUgYGNsdXN0ZXJQcm9maWxlcmAgYXV0aG9ycywgdGhleSBzdGF0ZToKCj4gMTcsOTgwIGdlbmVzIGRldGVjdGVkIGluIGEgTWljcm9hcnJheSBzdHVkeSAKCldoZXJlIHRoZSBrZXkgcGhyYXNlIGlzICoqZ2VuZXMgZGV0ZWN0ZWQqKi4gCgpJZiB3ZSB3ZXJlIHVuYWJsZSB0byBpbmNsdWRlIGEgZ2VuZSBpbiBvbmUgb2Ygb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zIGJlY2F1c2UsIGZvciBleGFtcGxlLCBpdCBoYWQgbG93IG1lYW4gZXhwcmVzc2lvbiBpbiBvdXIgZXhwZXJpbWVudCBhbmQgdGhlcmVmb3JlIHdhcyBmaWx0ZXJlZCBvdXQgaW4gb3VyIGB0aWR5cjo6ZHJvcF9uYSgpYCBzdGVwLCB3ZSBzaG91bGRuJ3QgaW5jbHVkZSBpbiBvdXIgYmFja2dyb3VuZCBzZXQuICAKCldlIGNhbiB1c2UgYW5vdGhlciBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIGBpbnRlcnNlY3QoKWAsIHRvIGdldCBvdXIgYmFja2dyb3VuZCBzZXQgb2YgZ2VuZXMgdGhhdCB3ZXJlIGluY2x1ZGVkIGluIGJvdGggY29tcGFyaXNvbnMuCgpgYGB7ciBnZXRfYmFja2dyb3VuZF9zZXR9CiMgaW50ZXJzZWN0KCkgd2lsbCByZXR1cm4gdGhlIGdlbmVzIGluIGJvdGggc2V0cyAtIHdlIGFyZSB1c2luZyB0aGUgZW50aXJlIGRhdGEKIyBmcmFtZSBoZXJlIChjb21wbGV0ZSBjYXNlcyksIG5vdCBqdXN0IHRoZSBzaWduaWZpY2FudCBnZW5lcwpiYWNrZ3JvdW5kX3NldCA8LSBpbnRlcnNlY3QodnNfbG93X2RmJGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdnNfdW5zb3J0ZWRfZGYkZ2VuZV9zeW1ib2wpCgojIFJlbW92ZSBhbnl0aGluZyB0aGF0IGNvdWxkbid0IHJlbGlhYmx5IGJlIG1lYXN1cmVkL2Fzc2Vzc2VkIGluIGJvdGggZnJvbSB0aGUKIyBnZW5lcyBvZiBpbnRlcmVzdCBsaXN0IC0gdXNpbmcgaW50ZXJzZWN0KCkgd2lsbCBkcm9wIGFueXRoaW5nIGluIHRoZSBmaXJzdCBzZXQKIyB0aGF0IGlzbid0IGFsc28gaW4gdGhlIHNlY29uZCBzZXQKZ2VuZXNfZm9yX29yYSA8LSBpbnRlcnNlY3QoZ2VuZXNfZm9yX29yYSwgYmFja2dyb3VuZF9zZXQpCmBgYAoKIyMjIyBSdW4gYGVucmljaGVyKClgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBiYWNrZ3JvdW5kIHNldCwgb3VyIGdlbmVzIG9mIGludGVyZXN0LCBhbmQgb3VyIHBhdGh3YXkgaW5mb3JtYXRpb24sIHdlJ3JlIHJlYWR5IHRvIHJ1biBPUkEgdXNpbmcgdGhlIGBlbnJpY2hlcigpYCBmdW5jdGlvbi4KCmBgYHtyIGtlZ2dfb3JhfQprZWdnX29yYV9yZXN1bHRzIDwtIGVucmljaGVyKAogIGdlbmUgPSBnZW5lc19mb3Jfb3JhLCAgIyBHZW5lcyBvZiBpbnRlcmVzdAogIHB2YWx1ZUN1dG9mZiA9IDAuMDUsICAKICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgICMgRkRSCiAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kX3NldCwgICMgQmFja2dyb3VuZCBzZXQKICAjIFRoZSBwYXRod2F5IGluZm9ybWF0aW9uIHNob3VsZCBiZSBhIGRhdGEgZnJhbWUgd2l0aCBhIHRlcm0gbmFtZSBvciAKICAjIGlkZW50aWZpZXIgYW5kIHRoZSBnZW5lIGlkZW50aWZpZXJzCiAgVEVSTTJHRU5FID0gZHBseXI6OnNlbGVjdChtbV9rZWdnX2RmLCAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc19uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zeW1ib2wpCikKYGBgCgoqTm90ZTogdXNpbmcgYGVucmljaEtFR0coKWAgaXMgYSBzaG9ydGN1dCBmb3IgZG9pbmcgT1JBIHVzaW5nIEtFR0csIGJ1dCB0aGUgYXBwcm9hY2ggd2UgY292ZXJlZCBoZXJlIGNhbiBiZSB1c2VkIHdpdGggYW55IGdlbmUgc2V0cyB5b3UnZCBsaWtlISoKCldoYXQgaXMgcmV0dXJuZWQgYnkgYGVucmljaGVyKClgPwoKYGBge3Igdmlld19rZWdnX29yYSwgZXZhbCA9IEZBTFNFfQpWaWV3KGtlZ2dfb3JhX3Jlc3VsdHMpCmBgYAoKVGhlIGluZm9ybWF0aW9uIHdlJ3JlIG1vc3QgbGlrZWx5IGludGVyZXN0ZWQgaW4gaXMgaW4gdGhlIGByZXN1bHRzYCBzbG90LgpMZXQncyBjb252ZXJ0IHRoaXMgaW50byBhIGRhdGEgZnJhbWUgdGhhdCB3ZSBjYW4gd3JpdGUgdG8gZmlsZS4KCmBgYHtyIGtlZ2dfZGZ9CmtlZ2dfcmVzdWx0X2RmIDwtIGRhdGEuZnJhbWUoa2VnZ19vcmFfcmVzdWx0c0ByZXN1bHQpCmBgYAoKIyMjIyBWaXN1YWxpemluZyByZXN1bHRzCgpXZSBjYW4gdXNlIGEgZG90IHBsb3QgdG8gdmlzdWFsaXplIG91ciBzaWduaWZpY2FudCBlbnJpY2htZW50IHJlc3VsdHMuCgpgYGB7ciBkb3RwbG90LCBsaXZlID0gVFJVRX0KZW5yaWNocGxvdDo6ZG90cGxvdChrZWdnX29yYV9yZXN1bHRzKQpgYGAKCldlIGNhbiB1c2UgYW4gW1VwU2V0IHBsb3RdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzQ3MjA5OTMvKSB0byB2aXN1YWxpemUgdGhlICoqb3ZlcmxhcCoqIGJldHdlZW4gdGhlIGdlbmUgc2V0cyB0aGF0IHdlcmUgcmV0dXJuZWQgYXMgc2lnbmlmaWNhbnQuCgpgYGB7ciB1cHNldHBsb3QsIGxpdmUgPSBUUlVFfQplbnJpY2hwbG90Ojp1cHNldHBsb3Qoa2VnZ19vcmFfcmVzdWx0cykKYGBgCgpXZSBjYW4gc2VlIHRoYXQgc29tZSBvZiB0aGUgRE5BIHJlcGFpciBwYXRod2F5cyBzaGFyZSBnZW5lcy4KR2VuZSBzZXRzIG9yIHBhdGh3YXlzIGFyZW4ndCBpbmRlcGVuZGVudCwgZWl0aGVyIQpTb21ldGltZXMgbXVsdGlwbGUgcGF0aHdheXMgdGhhdCBzaG93IHVwIGluIG91ciByZXN1bHRzIGFzIHNpZ25pZmljYW50IGFyZSBpbmRpY2F0aXZlIG9mIG9ubHkgYSBoYW5kZnVsIG9mIGdlbmVzIGluIG91ciBnZW5lIGxpc3QuCgpXZSBjYW4gbG9vayBhdCB0aGUgYGdlbmVJRGAgY29sdW1uIG9mIG91ciByZXN1bHRzIHRvIHNlZSB3aGF0IGdlbmVzIG92ZXJsYXA7IGl0J3MgYSBnb29kIGlkZWEgdG8gdGFrZSBhIGxvb2suCgpgYGB7ciBsb29rX2F0X2dlbmVfaWRzLCBsaXZlID0gVFJVRSwgcm93bmFtZXMucHJpbnQgPSBGQUxTRX0Ka2VnZ19yZXN1bHRfZGYgJT4lCiAgIyBVc2UgZHBseXI6OnNlbGVjdCgpIC0gdGhlIG5hbWUgb2YgdGhlIHBhdGh3YXkgaXMgaW4gdGhlIElEIGNvbHVtbgogIGRwbHlyOjpzZWxlY3QoSUQsIGdlbmVJRCkKYGBgCgojIyMjIFdyaXRlIHJlc3VsdHMgdG8gZmlsZQoKYGBge3Igd3JpdGVfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CnJlYWRyOjp3cml0ZV90c3Yoa2VnZ19yZXN1bHRfZGYsIGZpbGUgPSBrZWdnX3Jlc3VsdHNfZmlsZSkKYGBgCgojIyBTZXNzaW9uIEluZm8gCgpgYGB7ciBzZXNzaW9uX2luZm99CnNlc3Npb25JbmZvKCkKYGBgCg==
+LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogT3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMAotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFBlcmZvcm0gZ2VuZSBpZGVudGlmaWVyIGNvbnZlcnNpb24gd2l0aCBbYEFubm90YXRpb25EQklgIGFubm90YXRpb24gcGFja2FnZXNdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0Fubm90YXRpb25EYmkvaW5zdC9kb2MvSW50cm9Ub0Fubm90YXRpb25QYWNrYWdlcy5wZGYpCi0gQWNjZXNzIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSBnZW5lIHNldCBjb2xsZWN0aW9uc10oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25zLmpzcCkgdmlhIHRoZSBgbXNpZ2RicmAgcGFja2FnZQotIFByZXBhcmUgZ2VuZSBzZXRzIGZvciBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzLCBpbmNsdWRpbmcgYW4gYXBwcm9wcmlhdGUgYmFja2dyb3VuZCBzZXQKLSBQZXJmb3JtIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgd2l0aCB0aGUgYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZQoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBjb3ZlciBhIHR5cGUgb2YgcGF0aHdheSBvciBnZW5lIHNldCBhbmFseXNpcyBjYWxsZWQgb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcyAoT1JBKS4KVGhlIGlkZWEgYmVoaW5kIE9SQSBpcyByZWxhdGl2ZWx5IHN0cmFpZ2h0Zm9yd2FyZDogZ2l2ZW4gYSBzZXQgb2YgZ2VuZXMsIGRvIHRoZXNlIGdlbmVzIG92ZXJsYXAgd2l0aCBhIHBhdGh3YXkgbW9yZSB0aGFuIHdlIGV4cGVjdCBieSBjaGFuY2U/ClRoZSBzaW1wbGljaXR5IG9mIG9ubHkgcmVxdWlyaW5nIGFuIGlucHV0IGdlbmUgc2V0IChzb3J0IG9mLCBtb3JlIG9uIHRoYXQgYmVsb3cpIGNhbiBiZSBhdHRyYWN0aXZlLgoKT1JBIGhhcyBzb21lIGxpbWl0YXRpb25zLCBvdXRsaW5lZCBuaWNlbHkgKGFuZCBtb3JlIGV4dGVuc2l2ZWx5ISkgaW4gW0toYXRyaSBfZXQgYWwuXyAoMjAxMildKCBodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMjM3NSkuCk9uZSBvZiB0aGUgbWFpbiBpc3N1ZXMgd2l0aCBPUkEgaXMgdGhhdCB0eXBpY2FsbHkgYWxsIGdlbmVzIGFyZSB0cmVhdGVkIGFzIGVxdWFsIC0tIHRoZSBjb250ZXh0IG9mIHRoZSBtYWduaXR1ZGUgb2YgYSBjaGFuZ2Ugd2UgbWF5IGJlIG1lYXN1cmluZyBpcyByZW1vdmVkIGFuZCBlYWNoIGdlbmUgaXMgdHJlYXRlZCBhcyBpbmRlcGVuZGVudCwgd2hpY2ggY2FuIHNvbWV0aW1lcyByZXN1bHQgaW4gYW4gaW5jb3JyZWN0IGVzdGltYXRlIG9mIHNpZ25pZmljYW5jZS4KCldlIHdpbGwgdXNlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2NsdXN0ZXJQcm9maWxlci5odG1sKSAoW1l1ICpldCBhbC4qIDIwMTJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDg5L29taS4yMDExLjAxMTguKSkgdG8gcGVyZm9ybSBPUkEuCmBjbHVzdGVyUHJvZmlsZXJgIGhhcyBtYW55IGJ1aWx0LWluIGZ1bmN0aW9ucyB0aGF0IHdpbGwgcnVuIGEgc3BlY2lmaWMgdHlwZSBvZiBhbmFseXNpcyB1c2luZyBhIHNwZWNpZmljIHNvdXJjZSBvZiBwYXRod2F5cy9nZW5lIHNldHMgYXV0b21hdGljYWxseSwgYnV0IGZvciBvdXIgcHVycG9zZXMgd2UncmUgZ29pbmcgdG8ga2VlcCB0aGluZ3MgYXMgZ2VuZXJhbCBhcyBwb3NzaWJsZS4KU2VlIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmAgYm9va10oaHR0cHM6Ly95dWxhYi1zbXUuZ2l0aHViLmlvL2NsdXN0ZXJQcm9maWxlci1ib29rL2luZGV4Lmh0bWwpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwYWNrYWdlJ3MgZnVsbCBzdWl0ZSBvZiBmdW5jdGlvbmFsaXR5LgoKQmVjYXVzZSBkaWZmZXJlbnQgYmlvaW5mb3JtYXRpY3MgdG9vbHMgb2Z0ZW4gcmVxdWlyZSBkaWZmZXJlbnQgdHlwZXMgb2YgZ2VuZSBpZGVudGlmaWVycywgd2UnbGwgYWxzbyBjb3ZlciBob3cgdG8gY29udmVydCBiZXR3ZWVuIGdlbmUgaWRlbnRpZmllcnMgdXNpbmcgW2BBbm5vdGF0aW9uRGJpYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0Fubm90YXRpb25EYmkuaHRtbCkgQmlvY29uZHVjdG9yIHBhY2thZ2VzIGluIHRoaXMgbm90ZWJvb2suCkNoZWNrIG91dCB0aGUgW19Bbm5vdGF0aW9uRGJpOiBJbnRyb2R1Y3Rpb24gVG8gQmlvY29uZHVjdG9yIEFubm90YXRpb24gUGFja2FnZXNfIChDYXJsc29uIDIwMjAuKSB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvQW5ub3RhdGlvbkRiaS9pbnN0L2RvYy9JbnRyb1RvQW5ub3RhdGlvblBhY2thZ2VzLnBkZikgZm9yIG1vcmUgaW5mb3JtYXRpb24uCgojIyMjIE90aGVyIHJlc291cmNlcwoKKiBGb3IgYW5vdGhlciBleGFtcGxlIHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgLCBzZWUgW19JbnRybyB0byBER0U6IEZ1bmN0aW9uYWwgQW5hbHlzaXMuXyBmcm9tIEhhcnZhcmQgQ2hhbiBCaW9pbmZvcm1hdGljcyBDb3JlIFRyYWluaW5nLl0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDlfZnVuY3Rpb25hbF9hbmFseXNpcy5odG1sKQoqIFtgV2ViR2VzdGFsdFJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvV2ViR2VzdGFsdFIvKSBpcyBhbm90aGVyIFIgcGFja2FnZSB0aGF0IGNhbiBiZSB1c2VkIGZvciBPUkEuCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMKCmBgYHtyIGxpYnJhcmllc30KIyBQYWNrYWdlIHdlJ2xsIHVzZSBmb3IgT1JBCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQojIFBhY2thZ2UgdGhhdCBjb250YWlucyBNU2lnREIgZ2VuZSBzZXRzIGluIHRpZHkgZm9ybWF0CmxpYnJhcnkobXNpZ2RicikKIyBNdXMgbXVzY3VsdXMgYW5ub3RhdGlvbiBwYWNrYWdlIHdlJ2xsIHVzZSBmb3IgZ2VuZSBpZGVudGlmaWVyIGNvbnZlcnNpb24KbGlicmFyeShvcmcuTW0uZWcuZGIpCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKIyMjIyBEaXJlY3RvcmllcwoKYGBge3IgY3JlYXRlX29yYV9kaXJlY3RvcnksIGxpdmUgPSBUUlVFfQojIFdlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBzcGVjaWZpY2FsbHkgaG9sZCB0aGUgT1JBIHJlc3VsdHMgaWYgaXQgZG9lc24ndAojIGV4aXN0IHlldApyZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAibGV1a2VtaWEiKQppZiAoIWRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCiMjIyMgSW5wdXQgZmlsZXMKCkZvciBvdXIgT1JBIGV4YW1wbGUsIHdlJ3JlIGdvaW5nIHRvIHVzZSB0d28gdGFibGVzIG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gKERHRSkgYW5hbHlzaXMgcmVzdWx0cy4KCmBgYHtyIGlucHV0X2RpcmVjdG9yeX0KaW5wdXRfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJsZXVrZW1pYSIpCgojIFRoaXMgZmlsZSBjb250YWlucyB0aGUgREdFIHJlc3VsdHMgZm9yIGEgY2VsbCBwb3B1bGF0aW9uIHdpdGggaGlnaCBzdGVtIGNlbGwKIyBjYXBhY2l0eSBhcyBjb21wYXJlZCB0byBhIGNlbGwgcG9wdWxhdGlvbgp2c19sb3dfZmlsZSA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hfY2FwYWNpdHlfdnNfbG93X2NhcGFjaXR5X3Jlc3VsdHMudHN2IikKCiMgVGhpcyBmaWxlIGNvbnRhaW5zIHRoZSBER0UgcmVzdWx0cyBmb3IgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24KIyB2cy4gdW5zb3J0ZWQgY2VsbHMgKGUuZy4sIGEgbWl4dHVyZSBvZiBjYXBhY2l0aWVzKQp2c191bnNvcnRlZF9maWxlIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoX2NhcGFjaXR5X3ZzX3Vuc29ydGVkX3Jlc3VsdHMudHN2IikKYGBgCgojIyMjIE91dHB1dCBmaWxlcwoKV2UnbGwgc2F2ZSB0aGUgdGFibGUgb2YgT1JBIHJlc3VsdHMgKGUuZy4sIHAtdmFsdWVzKS4KCmBgYHtyIG91dHB1dF9maWxlLCBsaXZlID0gVFJVRX0Ka2VnZ19yZXN1bHRzX2ZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAibGV1a2VtaWFfa2VnZ19vcmFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgc2V0cwoKV2Ugd2lsbCB1c2UgZ2VuZSBzZXRzIGZyb20gdGhlIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKV0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2luZGV4LmpzcCkgZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlIChbU3VicmFtYW5pYW4sIFRhbWF5byAqZXQgYWwuKiAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpKS4KVGhlIFtgbXNpZ2RicmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tc2lnZGJyL2luZGV4Lmh0bWwpIHBhY2thZ2UgY29udGFpbnMgTVNpZ0RCIGRhdGFzZXRzIGFscmVhZHkgaW4gdGhlIHRpZHkgZm9ybWF0IHJlcXVpcmVkIGJ5IGBjbHVzdGVyUHJvZmlsZXJgIGFuZCBzdXBwb3J0cyBtdWx0aXBsZSBvcmdhbmlzbXMuCgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IG9yZ2FuaXNtcyB0aGUgcGFja2FnZSBzdXBwb3J0cy4KCmBgYHtyIHNob3dfc3BlY2llc30KbXNpZ2Ricl9zcGVjaWVzKCkKYGBgCgpUaGUgcmVzdWx0cyB3ZSdyZSBpbnRlcmVzdGVkIGluIGhlcmUgY29tZSBmcm9tIG1vdXNlIHNhbXBsZXMsIHNvIHdlIGNhbiBvYnRhaW4ganVzdCB0aGUgZ2VuZSBzZXRzIHJlbGV2YW50IHRvIF9NLiBtdXNjdWx1c18gd2l0aCB0aGUgYHNwZWNpZXNgIGFyZ3VtZW50IHRvIGBtc2lnZGJyKClgLgoKYGBge3IgbW1fZGYsIGxpdmUgPSBUUlVFfQptbV9tc2lnZGJfZGYgPC0gbXNpZ2RicihzcGVjaWVzID0gIk11cyBtdXNjdWx1cyIpCmBgYAoKTVNpZ0RCIGNvbnRhaW5zIDggZGlmZmVyZW50IGdlbmUgc2V0IGNvbGxlY3Rpb25zLgoKICAgIEg6IGhhbGxtYXJrIGdlbmUgc2V0cwogICAgQzE6IHBvc2l0aW9uYWwgZ2VuZSBzZXRzCiAgICBDMjogY3VyYXRlZCBnZW5lIHNldHMKICAgIEMzOiBtb3RpZiBnZW5lIHNldHMKICAgIEM0OiBjb21wdXRhdGlvbmFsIGdlbmUgc2V0cwogICAgQzU6IEdPIGdlbmUgc2V0cwogICAgQzY6IG9uY29nZW5pYyBzaWduYXR1cmVzCiAgICBDNzogaW1tdW5vbG9naWMgc2lnbmF0dXJlcwoKSW4gdGhpcyBleGFtcGxlLCB3ZSB3aWxsIHVzZSBjYW5vbmljYWwgcGF0aHdheXMgd2hpY2ggYXJlIChbcmVmXShodHRwczovL3d3dy5nc2VhLW1zaWdkYi5vcmcvZ3NlYS9tc2lnZGIvY29sbGVjdGlvbnMuanNwKSk6Cgo+IEdlbmUgc2V0cyBmcm9tIHBhdGh3YXkgZGF0YWJhc2VzLiBVc3VhbGx5LCB0aGVzZSBnZW5lIHNldHMgYXJlIGNhbm9uaWNhbCByZXByZXNlbnRhdGlvbnMgb2YgYSBiaW9sb2dpY2FsIHByb2Nlc3MgY29tcGlsZWQgYnkgZG9tYWluIGV4cGVydHMuCgpBbmQgYXJlIGEgc3Vic2V0IG9mIGBDMjogY3VyYXRlZCBnZW5lIHNldHNgLgpTcGVjaWZpY2FsbHksIHdlIHdpbGwgdXNlIHRoZSBbS0VHRyAoS3lvdG8gRW5jeWNsb3BlZGlhIG9mIEdlbmVzIGFuZCBHZW5vbWVzKV0oaHR0cHM6Ly93d3cuZ2Vub21lLmpwL2tlZ2cvKSBwYXRod2F5cy4KCmBgYHtyIGZpbHRlcl90b19rZWdnfQojIEZpbHRlciB0aGUgbW91c2UgZGF0YSBmcmFtZSB0byB0aGUgS0VHRyBwYXRod2F5cyB0aGF0IGFyZSBpbmNsdWRlZCBpbiB0aGUKIyBjdXJhdGVkIGdlbmUgc2V0cwptbV9rZWdnX2RmIDwtIG1tX21zaWdkYl9kZiB8PgogIGRwbHlyOjpmaWx0ZXIoZ3NfY2F0ID09ICJDMiIsICAjIGN1cmF0ZWQgZ2VuZSBzZXRzCiAgICAgICAgICAgICAgICBnc19zdWJjYXQgPT0gIkNQOktFR0ciKSAgIyBLRUdHIHBhdGh3YXlzCmBgYAoKKk5vdGU6IFdlIGNvdWxkIHNwZWNpZmllZCB0aGF0IHdlIHdhbnRlZCB0aGUgS0VHRyBnZW5lIHNldHMgdXNpbmcgdGhlIGBjYXRlZ29yeWAgYW5kIGBzdWJjYXRlZ29yeWAgYXJndW1lbnRzIG9mIGBtc2lnZGJyKClgLCBidXQgd2UncmUgZ29pbmcgZm9yIGdlbmVyYWwgc3RlcHMhKgoKYGBge3IgbW1fa2VnZ19jb2x1bW5zfQpjb2xuYW1lcyhtbV9rZWdnX2RmKQpgYGAKClRoZSBgY2x1c3RlclByb2ZpbGVyYCBmdW5jdGlvbiB3ZSB3aWxsIHVzZSByZXF1aXJlcyBhIGRhdGEgZnJhbWUgd2l0aCB0d28gY29sdW1ucywgd2hlcmUgb25lIGNvbHVtbiBjb250YWlucyB0aGUgdGVybSBpZGVudGlmaWVyIG9yIG5hbWUgYW5kIG9uZSBjb2x1bW4gY29udGFpbnMgZ2VuZSBpZGVudGlmaWVycyB0aGF0IG1hdGNoIG91ciBnZW5lIGxpc3RzIHdlIHdhbnQgdG8gY2hlY2sgZm9yIGVucmljaG1lbnQuCk91ciBkYXRhIGZyYW1lIHdpdGggS0VHRyB0ZXJtcyBjb250YWlucyBFbnRyZXogSURzIGFuZCBnZW5lIHN5bWJvbHMuCgojIyBSZWFkIGluIERHRSByZXN1bHRzIGFuZCBwcmVwCgpgYGB7ciByZWFkX2luX2RnZV9yZXN1bHRzLCBsaXZlID0gVFJVRX0KdnNfbG93X2RmIDwtIHJlYWRyOjpyZWFkX3Rzdih2c19sb3dfZmlsZSkKYGBgCgpMZXQncyB0YWtlIGEgcGVlayBhdCB0aGUgdG9wIG9mIHRoZSBER0UgcmVzdWx0cyBkYXRhIGZyYW1lLgoKYGBge3IgcGVla19hdF9kZ2VfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CmhlYWQodnNfbG93X2RmKQpgYGAKCiMjIyBHZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbgoKT3VyIGRhdGEgZnJhbWUgb2YgREdFIHJlc3VsdHMgY29udGFpbnMgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLgpTbyB3ZSB3aWxsIG5lZWQgdG8gY29udmVydCBmcm9tIHRoZXNlIGlkZW50aWZpZXJzIGludG8gZWl0aGVyIHRoZSBnZW5lIHN5bWJvbHMgb3IgRW50cmV6IElEcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBkYXRhIHdlIGV4dHJhY3RlZCB3aXRoIGBtc2lnZGJyKClgLgoKV2UncmUgZ29pbmcgdG8gY29udmVydCBvdXIgaWRlbnRpZmllcnMgdG8gZ2VuZSBzeW1ib2xzIGJlY2F1c2UgdGhleSBhcmUgYSBiaXQgbW9yZSBodW1hbiByZWFkYWJsZSwgYnV0IHlvdSBjYW4sIHdpdGggdGhlIGNoYW5nZSBvZiBhIHNpbmdsZSBhcmd1bWVudCwgdXNlIHRoZSBzYW1lIGNvZGUgdG8gY29udmVydCB0byBtYW55IG90aGVyIHR5cGVzIG9mIGlkZW50aWZpZXJzIQoKVGhlIGFubm90YXRpb24gcGFja2FnZSBgb3JnLk1tLmVnLmRiYCBjb250YWlucyBpbmZvcm1hdGlvbiBmb3IgZGlmZmVyZW50IGlkZW50aWZpZXJzLgpgb3JnLk1tLmVnLmRiYCBpcyBzcGVjaWZpYyB0byBfTXVzIG11c2N1bHVzXyAtLSB0aGlzIGlzIHdoYXQgdGhlIGBNbWAgaW4gdGhlIHBhY2thZ2UgbmFtZSBpcyByZWZlcmVuY2luZy4KVG8gcGVyZm9ybSBnZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbiBpbiBodW1hbiAoX0hvbW8gc2FwaWVuc18pIHdlIGNvdWxkIHVzZSBgb3JnLkhzLmVnLmRiYDsKd2Ugd291bGQgdXNlIGBvcmcuRHIuZWcuZGJgIGZvciB6ZWJyYWZpc2ggKF9EYW5pbyByZXJpb18pLgoKV2UgY2FuIHNlZSB3aGF0IHR5cGVzIG9mIElEcyBhcmUgYXZhaWxhYmxlIHRvIHVzIGluIGFuIGFubm90YXRpb24gcGFja2FnZSB3aXRoIGBrZXl0eXBlcygpYC4KCmBgYHtyIGtleXR5cGVzLCBsaXZlID0gVFJVRX0Ka2V5dHlwZXMob3JnLk1tLmVnLmRiKQpgYGAKCkV2ZW4gdGhvdWdoIHdlJ2xsIHVzZSB0aGlzIHBhY2thZ2UgdG8gY29udmVydCBmcm9tIEVuc2VtYmwgZ2VuZSBJRHMgKGBFTlNFTUJMYCkgdG8gZ2VuZSBzeW1ib2xzIChgU1lNQk9MYCksIHdlIGNvdWxkIGp1c3QgYXMgZWFzaWx5IHVzZSBpdCB0byBjb252ZXJ0IGZyb20gYW4gRW5zZW1ibCB0cmFuc2NyaXB0IElEIChgRU5TRU1CTFRSQU5TYCkgdG8gRW50cmV6IElEcyAoYEVOVFJFWklEYCkuCgpUaGUgZnVuY3Rpb24gd2Ugd2lsbCB1c2UgdG8gbWFwIGZyb20gRW5zZW1ibCBnZW5lIElEcyB0byBnZW5lIHN5bWJvbHMgaXMgY2FsbGVkIGBtYXBJZHMoKWAuCgpgYGB7ciBtYXBfdG9fc3ltYm9sfQojIFRoaXMgcmV0dXJucyBhIG5hbWVkIHZlY3RvciB3aGljaCB3ZSBjYW4gY29udmVydCB0byBhIGRhdGEgZnJhbWUsIHdoZXJlCiMgdGhlIGtleXMgKEVuc2VtYmwgSURzKSBhcmUgdGhlIG5hbWVzCnN5bWJvbHNfdmVjdG9yIDwtIG1hcElkcyhvcmcuTW0uZWcuZGIsICAjIFNwZWNpZnkgdGhlIGFubm90YXRpb24gcGFja2FnZQogICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdmVjdG9yIG9mIGdlbmUgaWRlbnRpZmllcnMgd2Ugd2FudCB0bwogICAgICAgICAgICAgICAgICAgICAgICAgIyBtYXAKICAgICAgICAgICAgICAgICAgICAgICAgIGtleXMgPSB2c19sb3dfZGYkR2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICMgV2hhdCB0eXBlIG9mIGdlbmUgaWRlbnRpZmllcnMgd2UncmUgc3RhcnRpbmcKICAgICAgICAgICAgICAgICAgICAgICAgICMgd2l0aAogICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJFTlNFTUJMIiwKICAgICAgICAgICAgICAgICAgICAgICAgICMgVGhlIHR5cGUgb2YgZ2VuZSBpZGVudGlmaWVyIHdlIHdhbnQgcmV0dXJuZWQKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbiA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgIyBJbiB0aGUgY2FzZSBvZiAxOm1hbnkgbWFwcGluZ3MsIHJldHVybiB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICMgZmlyc3Qgb25lLiBUaGlzIGlzIGRlZmF1bHQgYmVoYXZpb3IhCiAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aVZhbHMgPSAiZmlyc3QiKQoKIyBXZSB3b3VsZCBsaWtlIGEgZGF0YSBmcmFtZSB3ZSBjYW4gam9pbiB0byB0aGUgREdFIHJlc3VsdHMKc3ltYm9sc19kZiA8LSBkYXRhLmZyYW1lKAogIGVuc2VtYmxfaWQgPSBuYW1lcyhzeW1ib2xzX3ZlY3RvciksCiAgZ2VuZV9zeW1ib2wgPSBzeW1ib2xzX3ZlY3RvcgopCmBgYAoKVGhpcyBtZXNzYWdlIGlzIGxldHRpbmcgdXMga25vdyB0aGF0IHNvbWV0aW1lcyBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMgd2lsbCBtYXAgdG8gbXVsdGlwbGUgZ2VuZSBzeW1ib2xzLgpJbiB0aGlzIGNhc2UsIGl0J3MgYWxzbyBwb3NzaWJsZSB0aGF0IGEgZ2VuZSBzeW1ib2wgd2lsbCBtYXAgdG8gbXVsdGlwbGUgRW5zZW1ibCBJRHMuCgpOb3cgd2UgYXJlIHJlYWR5IHRvIGFkZCB0aGUgZ2VuZSBzeW1ib2xzIHRvIG91ciBkYXRhIGZyYW1lIHdpdGggdGhlIERHRSByZXN1bHRzLgpXZSBjYW4gdXNlIGEgX2pvaW5fIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSB0byBkbyB0aGlzLCB3aGljaCB3aWxsIHVzZSB0aGUgRW5zZW1ibCBnZW5lIElEcyBpbiBib3RoIGRhdGEgZnJhbWVzIHRvIGRldGVybWluZSBob3cgdG8gam9pbiB0aGUgcm93cy4KCkxldCdzIGRvIHRoaXMgZmlyc3QgZm9yIHRoZSBjb21wYXJpc29uIHRvIHRoZSBsb3cgc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24uCgpgYGB7ciBhZGRfc3ltYm9scywgbGl2ZSA9IFRSVUV9CnZzX2xvd19kZiA8LSBzeW1ib2xzX2RmIHw+CiAgIyBBbiAqaW5uZXIqIGpvaW4gd2lsbCBvbmx5IHJldHVybiByb3dzIHRoYXQgYXJlIGluIGJvdGggZGF0YSBmcmFtZXMKICBkcGx5cjo6aW5uZXJfam9pbih2c19sb3dfZGYsCiAgICAgICAgICAgICAgICAgICAgIyBUaGUgbmFtZSBvZiB0aGUgY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIEVuc2VtYmwgZ2VuZSBJRHMKICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBsZWZ0IGRhdGEgZnJhbWUgYW5kIHJpZ2h0IGRhdGEgZnJhbWUKICAgICAgICAgICAgICAgICAgICBieSA9IGMoImVuc2VtYmxfaWQiID0gIkdlbmUiKSkKYGBgCgojIyMgRHJvcCBgTkFgIHZhbHVlcwoKU29tZSBvZiB0aGVzZSByb3dzIGhhdmUgYE5BYCB2YWx1ZXMgaW4gYHBhZGpgLCB3aGljaCBbY2FuIGhhcHBlbiBmb3IgYSBudW1iZXIgb2YgcmVhc29uc10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3B2YWx1ZXNOQSkgaW5jbHVkaW5nIHdoZW4gYWxsIHNhbXBsZXMgaGF2ZSB6ZXJvIGNvdW50cyBvciBhIGdlbmUgaGFzIGxvdyBtZWFuIGV4cHJlc3Npb24uCgpMZXQncyBmaWx0ZXIgdG8gcm93cyB0aGF0IF9kbyBub3QgaGF2ZSBhbnkgYE5BYF8gdXNpbmcgYSBmdW5jdGlvbiBgdGlkeXI6OmRyb3BfbmEoKWAuClRoaXMgd2lsbCBhbHNvIGRyb3AgZ2VuZXMgdGhhdCBoYXZlIGFuIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVyIGJ1dCBubyBnZW5lIHN5bWJvbCEKCmBgYHtyIGNvbXBsZXRlX2Nhc2VzfQojIFJlbW92ZSByb3dzIHRoYXQgYXJlIG5vdCBjb21wbGV0ZSAoZS5nLiwgY29udGFpbiBOQXMpIGJ5IGZpbHRlcmluZyB0byBvbmx5CiMgY29tcGxldGUgcm93cwp2c19sb3dfZGYgPC0gdnNfbG93X2RmIHw+CiAgdGlkeXI6OmRyb3BfbmEoKQpgYGAKCioqTm93IHdlJ2xsIHJlYWQgaW4gb3VyIGRhdGEgZnJhbWUgb2YgREdFIHJlc3VsdHMgZnJvbSBhbm90aGVyIGNvbXBhcmlzb24uKioKVG8gc2F2ZSB1cyBzb21lIHRpbWUgZHVyaW5nIGluc3RydWN0aW9uLCB3ZSd2ZSBhbHJlYWR5IGRvbmUgdGhlIGdlbmUgaWRlbnRpZmllciBjb252ZXJzaW9uIGFuZCBmaWx0ZXJpbmcgdG8gcmVtb3ZlIGBOQWAgdmFsdWVzIGluIFt0aGlzIG5vdGVib29rXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL3BhdGh3YXktYW5hbHlzaXMvc2V0dXAvMDEtbGV1a2VtaWFfREdFLlJtZCkuCldlIHRvb2sgYSBkaWZmZXJlbnQgc2VyaWVzIG9mIHN0ZXBzIHRvIGFjaGlldmUgdGhlIHNhbWUgdGhpbmcsIHdoaWNoIGlzIG9mdGVuIHBvc3NpYmxlIGluIFIhCgoKYGBge3IgcmVhZF9pbl91bnNvcnRlZH0KdnNfdW5zb3J0ZWRfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZzX3Vuc29ydGVkX2ZpbGUpCmBgYAoKIyMgT3Zlci1yZXByZXNlbnRhdGlvbiBBbmFseXNpcyAoT1JBKQoKVG8gdGVzdCBmb3Igb3Zlci1yZXByZXNlbnRhdGlvbiwgd2UgY2FuIGNhbGN1bGF0ZSBhIHAtdmFsdWUgd2l0aCBhIGh5cGVyZ2VvbWV0cmljIHRlc3QgKFtyZWZdKGh0dHBzOi8veXVsYWItc211LmdpdGh1Yi5pby9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI292ZXItcmVwcmVzZW50YXRpb24tYW5hbHlzaXMpKS4KClwocCA9IDEgLSBcZGlzcGxheXN0eWxlXHN1bV97aSA9IDB9XntrLTF9XGZyYWN7IHtNIFxjaG9vc2UgaX17IHtOLU19IFxjaG9vc2Uge24taX0gfSB9IHsge04gXGNob29zZSBufSB9XCkKCldoZXJlIGBOYCBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBiYWNrZ3JvdW5kIGRpc3RyaWJ1dGlvbiwgYE1gIGlzIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gYSBwYXRod2F5LCBgbmAgaXMgdGhlIG51bWJlciBvZiBnZW5lcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiAob3VyIG1hcmtlciBnZW5lcyksIGFuZCBga2AgaXMgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IG92ZXJsYXAgYmV0d2VlbiB0aGUgcGF0aHdheSBhbmQgb3VyIG1hcmtlciBnZW5lcy4KCkJvcnJvd2luZyBhbiBleGFtcGxlIGZyb20gW19jbHVzdGVyUHJvZmlsZXI6IHVuaXZlcnNhbCBlbnJpY2htZW50IHRvb2wgZm9yIGZ1bmN0aW9uYWwgYW5kIGNvbXBhcmF0aXZlIHN0dWR5XyAoWXUgKV0oaHR0cDovL3l1bGFiLXNtdS50b3AvY2x1c3RlclByb2ZpbGVyLWJvb2svY2hhcHRlcjIuaHRtbCNvdmVyLXJlcHJlc2VudGF0aW9uLWFuYWx5c2lzKToKCj4gKipFeGFtcGxlKio6IFN1cHBvc2Ugd2UgaGF2ZSAxNyw5ODAgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBNaWNyb2FycmF5IHN0dWR5IGFuZCA1NyBnZW5lcyB3ZXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4gQW1vbmcgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMsIDI4IGFyZSBhbm5vdGF0ZWQgdG8gYSBnZW5lIHNldC4KCldlJ2xsIGNhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGBnZW5lX2luX2ludGVyZXN0YCBhbmQgZ2VuZXMgdGhhdCBhcmUgaW4gdGhlIGdlbmUgc2V0IGBpbl9nZW5lX3NldGAuCgpgYGB7ciBnZW5lX3RhYmxlfQpnZW5lX3RhYmxlIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZV9ub3RfaW50ZXJlc3QgPSBjKDI2MTMsIDE1MzEwKSwKICBnZW5lX2luX2ludGVyZXN0ID0gYygyOCwgMjkpCikKcm93bmFtZXMoZ2VuZV90YWJsZSkgPC0gYygiaW5fZ2VuZV9zZXQiLCAibm90X2luX2dlbmVfc2V0IikKCmdlbmVfdGFibGUKYGBgCgpXZSBjYW4gYXNzZXNzIGlmIHRoZSAyOCBvdmVybGFwcGluZyBnZW5lcyBtZWFuIHRoYXQgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiB0aGUgZ2VuZSBzZXQgd2l0aCB0aGUgaHlwZXJnZW9tZXRyaWMgZGlzdHJpYnV0aW9uLgpUaGlzIGNvcnJlc3BvbmRzIHRvIGEgb25lLXNpZGVkIEZpc2hlcidzIGV4YWN0IHRlc3QuCgpgYGB7ciBmaXNoZXJfdGVzdH0KZmlzaGVyLnRlc3QoZ2VuZV90YWJsZSwgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCmBgYAoKV2hlbiB3ZSB0ZXN0ICoqbXVsdGlwbGUgcGF0aHdheXMgb3IgZ2VuZSBzZXRzKiosIHRoZSBwLXZhbHVlcyB0aGVuIG5lZWQgdG8gYmUgKiphZGp1c3RlZCoqIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcuCgojIyMgSGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgT1JBCgpPdXIgREdFIHJlc3VsdHMgYXJlIGZyb20gZGF0YSBwdWJsaXNoZWQgYXMgcGFydCBvZiBbU2FjaHMgX2V0IGFsLl8gKDIwMTQpXShodHRwczovL2R4LmRvaS5vcmcvMTAuMTE4MiUyRmJsb29kLTIwMTMtMDgtNTIxNzA4KS4KVGhlIGF1dGhvcnMgc29ydGVkIHBvcHVsYXRpb25zIG9mIHByaW1hcnkgbGV1a2VtaWEgY2VsbHMgYW5kIGV4YW1pbmVkIHRoZSBzdGVtIGNlbGwgY2FwYWNpdHkgb2YgdGhlc2UgY2VsbCBwb3B1bGF0aW9ucy4KKFRoaXMgc3R1ZHkgbWF5IHNvdW5kIGZhbWlsaWFyIGlmIHlvdSd2ZSB3b3JrZWQgb24gb25lIG9mIG91ciBidWxrIFJOQS1zZXEgZXhlcmNpc2Ugbm90ZWJvb2tzIGluIHRoZSBwYXN0ISkKCldlIGNvbXBhcmVkIHRoZSBwb3B1bGF0aW9uIHRoYXQgdGhlIGF1dGhvcnMgaWRlbnRpZmllZCBhcyBoYXZpbmcgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgdG8gYSBsb3cgc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24uCldlIGFsc28gY29tcGFyZWQgdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IGNlbGxzIHRvIGEgbWl4IG9mIHBvcHVsYXRpb25zIChlLmcuLCB1bnNvcnRlZCBjZWxscykuCllvdSBjYW4gc2VlIHRoZSBjb2RlIGluIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL3BhdGh3YXktYW5hbHlzaXMvc2V0dXAvMDEtbGV1a2VtaWFfREdFLlJtZCkuCgpXZSdyZSBpbnRlcmVzdGVkIGluIHdoYXQgcGF0aHdheXMgYXJlIG92ZXItcmVwcmVzZW50ZWQgaW4gZ2VuZXMgdGhhdCBzcGVjaWZpY2FsbHkgZGlzdGluZ3Vpc2ggdGhlIGhpZ2ggY2FwYWNpdHkgcG9wdWxhdGlvbiBmcm9tIHRoZSBsb3cgY2FwYWNpdHkgcG9wdWxhdGlvbi4KCkxldCdzIGdlbmVyYXRlIGEgbGlzdCBvZiBnZW5lcyB0aGF0IGhhdmUgaGlnaGVyIGV4cHJlc3Npb24gaW4gdGhlIGhpZ2ggc3RlbSBjZWxsIGNhcGFjaXR5IHBvcHVsYXRpb24gY29tcGFyZWQgdG8gdGhlIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiwgYnV0IHdlJ2xsIGFsc28gd2FudCB0byBleGNsdWRlIGdlbmVzIHRoYXQgc2hvdyB1cCBpbiBvdXIgX290aGVyIGNvbXBhcmlzb25fIHRvIHVuc29ydGVkIGNlbGxzLgoKV2UnbGwgc3RhcnQgd2l0aCB0aGUgaGlnaCBzdGVtIGNlbGwgY2FwYWNpdHkgdnMuIGxvdyBzdGVtIGNlbGwgY2FwYWNpdHkgcG9wdWxhdGlvbiBjb21wYXJpc29uLgpHZW5lcyB3aXRoIHBvc2l0aXZlIGxvZzIgZm9sZC1jaGFuZ2VzIChMRkMpIHdpbGwgYmUgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIHRoZSBoaWdoIHN0ZW0gY2VsbCBjYXBhY2l0eSBjZWxscyBiYXNlZCBvbiBob3cgd2Ugc2V0IHVwIHRoZSBhbmFseXNpcy4KCmBgYHtyIGhpZ2hfY2FwYWNpdHlfZ2VuZXN9CnZzX2xvd19nZW5lcyA8LSB2c19sb3dfZGYgfD4KICAjIEZpbHRlciB0byB0aGUgcG9zaXRpdmUgTEZDIGFuZCBmaWx0ZXIgYmFzZWQgb24gc2lnbmlmaWNhbmNlIHRvbyAocGFkaikKICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSB8PgogICMgUmV0dXJuIGEgdmVjdG9yIG9mIGdlbmUgc3ltYm9scwogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKQpgYGAKCkFsdGhvdWdoIHdlJ3JlIHBpY2tpbmcgYSBfY29tbW9ubHkgdXNlZF8gY3V0b2ZmIChGRFIgPCAwLjA1KSwgaXQncyBzdGlsbCBhcmJpdHJhcnkgYW5kIHdlIGNvdWxkIGp1c3QgYXMgZWFzaWx5IHBpY2sgYSBkaWZmZXJlbnQgdGhyZXNob2xkIGZvciBvdXIgTEZDIHZhbHVlcy4KV2hlbiB3ZSBnZW5lcmF0ZSBsaXN0cyBvZiBnZW5lcyBvZiBpbnRlcmVzdCBmb3IgT1JBLCB3ZSB0eXBpY2FsbHkgcGljayBhbiBhcmJpdHJhcnkgY3V0b2ZmLgpUaGlzIGlzIG9uZSBvZiB0aGUgYXBwcm9hY2gncyB3ZWFrbmVzc2VzIC0tIHdlJ3ZlIHJlbW92ZWQgYWxsIG90aGVyIGNvbnRleHQuCgpOb3csIHdlJ2xsIHRha2UgdGhlIHNhbWUgc3RlcHMgZm9yIG91ciBvdGhlciByZXN1bHRzLgoKYGBge3IgdW5zb3J0ZWRfZ2VuZXNfdG9fcmVtb3ZlLCBsaXZlID0gVFJVRX0KdnNfdW5zb3J0ZWRfZ2VuZXMgPC0gdnNfdW5zb3J0ZWRfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgIHBhZGogPCAwLjA1KSB8PgogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKQpgYGAKCldlIHdhbnQgZ2VuZXMgdGhhdCBhcmUgaW4gdGhlIGZpcnN0IGNvbXBhcmlzb24gYnV0IG5vdCBpbiB0aGUgc2Vjb25kIQpXZSBjYW4gdXNlIGBzZXRkaWZmKClgLCBhIGJhc2UgUiBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIHRvIGdldCB0aGUgbGlzdCB0aGF0IHdlIHdhbnQuCgpgYGB7ciBzZXRkaWZmfQojIFdoYXQgZ2VuZXMgYXJlIGluIHRoZSBmaXJzdCBzZXQgYnV0ICpub3QqIGluIHRoZSBzZWNvbmQgc2V0CmdlbmVzX2Zvcl9vcmEgPC0gc2V0ZGlmZih2c19sb3dfZ2VuZXMsIHZzX3Vuc29ydGVkX2dlbmVzKQpgYGAKCiMjIyMgQmFja2dyb3VuZCBzZXQKCkFzIHdlIHNhdyBhYm92ZSwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUgcmVsaWVzIG9uIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGJhY2tncm91bmQgZGlzdHJpYnV0aW9uLgpTb21ldGltZXMgZm9sa3MgY29uc2lkZXIgZ2VuZXMgZnJvbSB0aGUgZW50aXJlIGdlbm9tZSB0byBjb21wcmlzZSB0aGUgYmFja2dyb3VuZCwgYnV0IGluIHRoZSBleGFtcGxlIGJvcnJvd2VkIGZyb20gdGhlIGBjbHVzdGVyUHJvZmlsZXJgIGF1dGhvcnMsIHRoZXkgc3RhdGU6Cgo+IDE3LDk4MCBnZW5lcyBkZXRlY3RlZCBpbiBhIE1pY3JvYXJyYXkgc3R1ZHkKCldoZXJlIHRoZSBrZXkgcGhyYXNlIGlzICoqZ2VuZXMgZGV0ZWN0ZWQqKi4KCklmIHdlIHdlcmUgdW5hYmxlIHRvIGluY2x1ZGUgYSBnZW5lIGluIG9uZSBvZiBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMgYmVjYXVzZSwgZm9yIGV4YW1wbGUsIGl0IGhhZCBsb3cgbWVhbiBleHByZXNzaW9uIGluIG91ciBleHBlcmltZW50IGFuZCB0aGVyZWZvcmUgd2FzIGZpbHRlcmVkIG91dCBpbiBvdXIgYHRpZHlyOjpkcm9wX25hKClgIHN0ZXAsIHdlIHNob3VsZG4ndCBpbmNsdWRlIGluIG91ciBiYWNrZ3JvdW5kIHNldC4KCldlIGNhbiB1c2UgYW5vdGhlciBmdW5jdGlvbiBmb3Igc2V0IG9wZXJhdGlvbnMsIGBpbnRlcnNlY3QoKWAsIHRvIGdldCBvdXIgYmFja2dyb3VuZCBzZXQgb2YgZ2VuZXMgdGhhdCB3ZXJlIGluY2x1ZGVkIGluIGJvdGggY29tcGFyaXNvbnMuCgpgYGB7ciBnZXRfYmFja2dyb3VuZF9zZXR9CiMgaW50ZXJzZWN0KCkgd2lsbCByZXR1cm4gdGhlIGdlbmVzIGluIGJvdGggc2V0cyAtIHdlIGFyZSB1c2luZyB0aGUgZW50aXJlIGRhdGEKIyBmcmFtZSBoZXJlIChjb21wbGV0ZSBjYXNlcyksIG5vdCBqdXN0IHRoZSBzaWduaWZpY2FudCBnZW5lcwpiYWNrZ3JvdW5kX3NldCA8LSBpbnRlcnNlY3QodnNfbG93X2RmJGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdnNfdW5zb3J0ZWRfZGYkZ2VuZV9zeW1ib2wpCgojIFJlbW92ZSBhbnl0aGluZyB0aGF0IGNvdWxkbid0IHJlbGlhYmx5IGJlIG1lYXN1cmVkL2Fzc2Vzc2VkIGluIGJvdGggZnJvbSB0aGUKIyBnZW5lcyBvZiBpbnRlcmVzdCBsaXN0IC0gdXNpbmcgaW50ZXJzZWN0KCkgd2lsbCBkcm9wIGFueXRoaW5nIGluIHRoZSBmaXJzdCBzZXQKIyB0aGF0IGlzbid0IGFsc28gaW4gdGhlIHNlY29uZCBzZXQKZ2VuZXNfZm9yX29yYSA8LSBpbnRlcnNlY3QoZ2VuZXNfZm9yX29yYSwgYmFja2dyb3VuZF9zZXQpCmBgYAoKIyMjIyBSdW4gYGVucmljaGVyKClgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBiYWNrZ3JvdW5kIHNldCwgb3VyIGdlbmVzIG9mIGludGVyZXN0LCBhbmQgb3VyIHBhdGh3YXkgaW5mb3JtYXRpb24sIHdlJ3JlIHJlYWR5IHRvIHJ1biBPUkEgdXNpbmcgdGhlIGBlbnJpY2hlcigpYCBmdW5jdGlvbi4KCmBgYHtyIGtlZ2dfb3JhfQprZWdnX29yYV9yZXN1bHRzIDwtIGVucmljaGVyKAogIGdlbmUgPSBnZW5lc19mb3Jfb3JhLCAgIyBHZW5lcyBvZiBpbnRlcmVzdAogIHB2YWx1ZUN1dG9mZiA9IDAuMDUsCiAgcEFkanVzdE1ldGhvZCA9ICJCSCIsICAjIEZEUgogIHVuaXZlcnNlID0gYmFja2dyb3VuZF9zZXQsICAjIEJhY2tncm91bmQgc2V0CiAgIyBUaGUgcGF0aHdheSBpbmZvcm1hdGlvbiBzaG91bGQgYmUgYSBkYXRhIGZyYW1lIHdpdGggYSB0ZXJtIG5hbWUgb3IKICAjIGlkZW50aWZpZXIgYW5kIHRoZSBnZW5lIGlkZW50aWZpZXJzCiAgVEVSTTJHRU5FID0gZHBseXI6OnNlbGVjdChtbV9rZWdnX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3NfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc3ltYm9sKQopCmBgYAoKKk5vdGU6IHVzaW5nIGBlbnJpY2hLRUdHKClgIGlzIGEgc2hvcnRjdXQgZm9yIGRvaW5nIE9SQSB1c2luZyBLRUdHLCBidXQgdGhlIGFwcHJvYWNoIHdlIGNvdmVyZWQgaGVyZSBjYW4gYmUgdXNlZCB3aXRoIGFueSBnZW5lIHNldHMgeW91J2QgbGlrZSEqCgpXaGF0IGlzIHJldHVybmVkIGJ5IGBlbnJpY2hlcigpYD8KCmBgYHtyIHZpZXdfa2VnZ19vcmEsIGV2YWwgPSBGQUxTRX0KVmlldyhrZWdnX29yYV9yZXN1bHRzKQpgYGAKClRoZSBpbmZvcm1hdGlvbiB3ZSdyZSBtb3N0IGxpa2VseSBpbnRlcmVzdGVkIGluIGlzIGluIHRoZSBgcmVzdWx0c2Agc2xvdC4KTGV0J3MgY29udmVydCB0aGlzIGludG8gYSBkYXRhIGZyYW1lIHRoYXQgd2UgY2FuIHdyaXRlIHRvIGZpbGUuCgpgYGB7ciBrZWdnX2RmfQprZWdnX3Jlc3VsdF9kZiA8LSBkYXRhLmZyYW1lKGtlZ2dfb3JhX3Jlc3VsdHNAcmVzdWx0KQpgYGAKCiMjIyMgVmlzdWFsaXppbmcgcmVzdWx0cwoKV2UgY2FuIHVzZSBhIGRvdCBwbG90IHRvIHZpc3VhbGl6ZSBvdXIgc2lnbmlmaWNhbnQgZW5yaWNobWVudCByZXN1bHRzLgoKYGBge3IgZG90cGxvdCwgbGl2ZSA9IFRSVUV9CmVucmljaHBsb3Q6OmRvdHBsb3Qoa2VnZ19vcmFfcmVzdWx0cykKYGBgCgpXZSBjYW4gdXNlIGFuIFtVcFNldCBwbG90XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM0NzIwOTkzLykgdG8gdmlzdWFsaXplIHRoZSAqKm92ZXJsYXAqKiBiZXR3ZWVuIHRoZSBnZW5lIHNldHMgdGhhdCB3ZXJlIHJldHVybmVkIGFzIHNpZ25pZmljYW50LgoKYGBge3IgdXBzZXRwbG90LCBsaXZlID0gVFJVRX0KZW5yaWNocGxvdDo6dXBzZXRwbG90KGtlZ2dfb3JhX3Jlc3VsdHMpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHNvbWUgb2YgdGhlIEROQSByZXBhaXIgcGF0aHdheXMgc2hhcmUgZ2VuZXMuCkdlbmUgc2V0cyBvciBwYXRod2F5cyBhcmVuJ3QgaW5kZXBlbmRlbnQsIGVpdGhlciEKU29tZXRpbWVzIG11bHRpcGxlIHBhdGh3YXlzIHRoYXQgc2hvdyB1cCBpbiBvdXIgcmVzdWx0cyBhcyBzaWduaWZpY2FudCBhcmUgaW5kaWNhdGl2ZSBvZiBvbmx5IGEgaGFuZGZ1bCBvZiBnZW5lcyBpbiBvdXIgZ2VuZSBsaXN0LgoKV2UgY2FuIGxvb2sgYXQgdGhlIGBnZW5lSURgIGNvbHVtbiBvZiBvdXIgcmVzdWx0cyB0byBzZWUgd2hhdCBnZW5lcyBvdmVybGFwOyBpdCdzIGEgZ29vZCBpZGVhIHRvIHRha2UgYSBsb29rLgoKYGBge3IgbG9va19hdF9nZW5lX2lkcywgbGl2ZSA9IFRSVUUsIHJvd25hbWVzLnByaW50ID0gRkFMU0V9CmtlZ2dfcmVzdWx0X2RmIHw+CiAgIyBVc2UgZHBseXI6OnNlbGVjdCgpIC0gdGhlIG5hbWUgb2YgdGhlIHBhdGh3YXkgaXMgaW4gdGhlIElEIGNvbHVtbgogIGRwbHlyOjpzZWxlY3QoSUQsIGdlbmVJRCkKYGBgCgojIyMjIFdyaXRlIHJlc3VsdHMgdG8gZmlsZQoKYGBge3Igd3JpdGVfcmVzdWx0cywgbGl2ZSA9IFRSVUV9CnJlYWRyOjp3cml0ZV90c3Yoa2VnZ19yZXN1bHRfZGYsIGZpbGUgPSBrZWdnX3Jlc3VsdHNfZmlsZSkKYGBgCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb25faW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK
@@ -1127,7 +3791,7 @@ Session Info
$(document).ready(function () {
$('.tabset-dropdown > .nav-tabs > li').click(function () {
- $(this).parent().toggleClass('nav-tabs-open')
+ $(this).parent().toggleClass('nav-tabs-open');
});
});
@@ -1143,6 +3807,9 @@ Session Info
-
Since this data frame of DGE results includes gene symbols, we do not
@@ -3118,7 +3124,7 @@
Differential gene expression results
will count the number of duplicate values.
-
+
sum(duplicated(dge_results_df$gene_symbol))
@@ -3141,7 +3147,7 @@ Removing duplicates
explore our filtering steps.
-
+
duplicated_gene_symbols <- dge_results_df |>
dplyr::filter(duplicated(gene_symbol)) |>
dplyr::pull(gene_symbol)
@@ -3151,18 +3157,16 @@ Removing duplicates
Now we’ll look at the values for the the duplicated gene symbols.
-
+
dge_results_df |>
dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
dplyr::arrange(gene_symbol)
-
-
We can see that the associated values vary for each row.
@@ -3185,7 +3189,7 @@ Removing duplicates
value.
-
+
filtered_dge_df <- dge_results_df |>
# Sort so that the highest absolute values of the log2 fold change are at the
# top
@@ -3198,20 +3202,18 @@ Removing duplicates
Let’s see what happened to our duplicate identifiers.
-
+
# Subset to & arrange by gene symbols that were duplicated in the original
# data frame of results
filtered_dge_df |>
dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
dplyr::arrange(gene_symbol)
-
-
Now we’re ready to prep our pre-ranked list for GSEA.
@@ -3223,7 +3225,7 @@ Pre-ranked list
identifiers. This is step 1 – gene-level statistics.
-
+
lfc_vector <- filtered_dge_df |>
# Extract a vector of `log2FoldChange` named by `gene_symbol`
dplyr::pull(log2FoldChange, name = gene_symbol)
@@ -3234,7 +3236,7 @@ Pre-ranked list
Let’s look at the top ranked values.
-
+
# Look at first entries of the log2 fold change vector
head(lfc_vector)
@@ -3247,7 +3249,7 @@ Pre-ranked list
And the bottom of the list.
-
+
# Look at the last entries of the log2 fold change vector
tail(lfc_vector)
@@ -3267,7 +3269,7 @@ Run GSEA
specific, commonly used gene sets (e.g., gseKEGG()
).
-
+
gsea_results <- GSEA(geneList = lfc_vector, # ordered ranked gene list
minGSSize = 25, # minimum gene set size
maxGSSize = 500, # maximum gene set set
@@ -3277,15 +3279,25 @@ Run GSEA
gs_name,
gene_symbol))
-
-using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).
-
-preparing geneSet collections...
-GSEA analysis...
-Warning: There are ties in the preranked stats (0.1% of the list).
-The order of those tied genes will be arbitrary, which may produce unexpected results.leading edge analysis...
-done...
-
+
+using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).
+
+
+preparing geneSet collections...
+
+
+GSEA analysis...
+
+
+Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (0.1% of the list).
+The order of those tied genes will be arbitrary, which may produce unexpected results.
+
+
+leading edge analysis...
+
+
+done...
+
The warning about ties means that there are multiple genes that have
@@ -3309,7 +3321,7 @@
Run GSEA
Let’s write these results to file.
-
+
gsea_results@result |> readr::write_tsv(gsea_results_file)
@@ -3328,14 +3340,14 @@ Highly Positive NES
higher expression value in MYCN amplified cell lines.
-
+
enrichplot::gseaplot(gsea_results,
geneSetID = "HALLMARK_MYC_TARGETS_V1",
title = "HALLMARK_MYC_TARGETS_V1",
color.line = "#0066FF")
-
-
+
+
@@ -3349,14 +3361,14 @@ Highly Negative NES
highly negative NES.
-
+
enrichplot::gseaplot(gsea_results,
geneSetID = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
title = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
color.line = "#0066FF")
-
-
+
+
@@ -3372,14 +3384,14 @@ A non-significant result
viewed earlier.
-
+
enrichplot::gseaplot(gsea_results,
geneSetID = "HALLMARK_P53_PATHWAY",
title = "HALLMARK_P53_PATHWAY",
color.line = "#0066FF")
-
-
+
+
@@ -3395,11 +3407,11 @@ A non-significant result
Session Info
-
+
sessionInfo()
-
-R version 4.4.0 (2024-04-24)
+
+R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
@@ -3408,9 +3420,12 @@ Session Info
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
locale:
- [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8 LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8
- [6] LC_MESSAGES=C.UTF-8 LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
-[11] LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
+ [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
+ [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
+ [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
+ [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
+ [9] LC_ADDRESS=C LC_TELEPHONE=C
+[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Etc/UTC
tzcode source: system (glibc)
@@ -3419,32 +3434,44 @@ Session Info
[1] stats graphics grDevices utils datasets methods base
other attached packages:
-[1] msigdbr_7.5.1 clusterProfiler_4.12.0
+[1] msigdbr_7.5.1 clusterProfiler_4.12.0 optparse_1.7.5
loaded via a namespace (and not attached):
- [1] DBI_1.2.2 shadowtext_0.1.3 gson_0.1.0 gridExtra_2.3 rlang_1.1.3
- [6] magrittr_2.0.3 DOSE_3.30.0 compiler_4.4.0 RSQLite_2.3.6 png_0.1-8
- [11] vctrs_0.6.5 reshape2_1.4.4 stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2
- [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3 ggraph_2.2.1 utf8_1.2.4
- [21] HDO.db_0.99.1 tzdb_0.4.0 enrichplot_1.24.0 UCSC.utils_1.0.0 purrr_1.0.2
- [26] bit_4.0.5 xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8 aplot_0.2.2
- [31] GenomeInfoDb_1.40.0 jsonlite_1.8.8 blob_1.2.4 BiocParallel_1.38.0 tweenr_2.0.3
- [36] parallel_4.4.0 R6_2.5.1 stringi_1.8.3 RColorBrewer_1.1-3 GOSemSim_2.30.0
- [41] knitr_1.46 Rcpp_1.0.12 readr_2.1.5 IRanges_2.38.0 Matrix_1.7-0
- [46] splines_4.4.0 igraph_2.0.3 tidyselect_1.2.1 qvalue_2.36.0 rstudioapi_0.16.0
- [51] viridis_0.6.5 codetools_0.2-20 lattice_0.22-6 tibble_3.2.1 plyr_1.8.9
- [56] Biobase_2.64.0 treeio_1.28.0 withr_3.0.0 KEGGREST_1.44.0 gridGraphics_0.5-1
- [61] scatterpie_0.2.2 polyclip_1.10-6 Biostrings_2.72.0 pillar_1.9.0 ggtree_3.12.0
- [66] stats4_4.4.0 ggfun_0.1.4 generics_0.1.3 vroom_1.6.5 hms_1.1.3
- [71] S4Vectors_0.42.0 ggplot2_3.5.1 munsell_0.5.1 scales_1.3.0 tidytree_0.4.6
- [76] glue_1.7.0 lazyeval_0.2.2 tools_4.4.0 data.table_1.15.4 fgsea_1.30.0
- [81] babelgene_22.9 fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4 tidygraph_1.3.1
- [86] cowplot_1.1.3 grid_4.4.0 tidyr_1.3.1 ape_5.8 AnnotationDbi_1.66.0
- [91] colorspace_2.1-0 nlme_3.1-164 GenomeInfoDbData_1.2.12 patchwork_1.2.0 ggforce_0.4.2
- [96] cli_3.6.2 fansi_1.0.6 viridisLite_0.4.2 dplyr_1.1.4 gtable_0.3.5
-[101] yulab.utils_0.1.4 digest_0.6.35 BiocGenerics_0.50.0 ggrepel_0.9.5 ggplotify_0.1.2
-[106] farver_2.1.1 memoise_2.0.1 lifecycle_1.0.4 httr_1.4.7 GO.db_3.19.1
-[111] bit64_4.0.5 MASS_7.3-60.2
+ [1] DBI_1.2.2 gson_0.1.0 shadowtext_0.1.3
+ [4] gridExtra_2.3 rlang_1.1.3 magrittr_2.0.3
+ [7] DOSE_3.30.0 compiler_4.4.1 RSQLite_2.3.6
+ [10] png_0.1-8 vctrs_0.6.5 reshape2_1.4.4
+ [13] stringr_1.5.1 pkgconfig_2.0.3 crayon_1.5.2
+ [16] fastmap_1.1.1 XVector_0.44.0 labeling_0.4.3
+ [19] ggraph_2.2.1 utf8_1.2.4 HDO.db_0.99.1
+ [22] rmarkdown_2.26 tzdb_0.4.0 enrichplot_1.24.0
+ [25] UCSC.utils_1.0.0 purrr_1.0.2 bit_4.0.5
+ [28] xfun_0.43 zlibbioc_1.50.0 cachem_1.0.8
+ [31] aplot_0.2.2 GenomeInfoDb_1.40.0 jsonlite_1.8.8
+ [34] blob_1.2.4 highr_0.10 BiocParallel_1.38.0
+ [37] tweenr_2.0.3 parallel_4.4.1 R6_2.5.1
+ [40] bslib_0.7.0 stringi_1.8.3 RColorBrewer_1.1-3
+ [43] jquerylib_0.1.4 GOSemSim_2.30.0 Rcpp_1.0.12
+ [46] knitr_1.46 readr_2.1.5 IRanges_2.38.0
+ [49] Matrix_1.7-0 splines_4.4.1 igraph_2.0.3
+ [52] tidyselect_1.2.1 qvalue_2.36.0 yaml_2.3.8
+ [55] viridis_0.6.5 codetools_0.2-20 lattice_0.22-6
+ [58] tibble_3.2.1 plyr_1.8.9 treeio_1.28.0
+ [61] Biobase_2.64.0 withr_3.0.0 KEGGREST_1.44.0
+ [64] evaluate_0.23 gridGraphics_0.5-1 scatterpie_0.2.2
+ [67] getopt_1.20.4 polyclip_1.10-6 Biostrings_2.72.0
+ [70] ggtree_3.12.0 pillar_1.9.0 stats4_4.4.1
+ [73] ggfun_0.1.4 generics_0.1.3 vroom_1.6.5
+ [76] hms_1.1.3 S4Vectors_0.42.0 ggplot2_3.5.1
+ [79] tidytree_0.4.6 munsell_0.5.1 scales_1.3.0
+ [82] glue_1.7.0 lazyeval_0.2.2 tools_4.4.1
+ [85] data.table_1.15.4 fgsea_1.30.0 babelgene_22.9
+ [88] fs_1.6.4 graphlayouts_1.1.1 fastmatch_1.1-4
+ [91] tidygraph_1.3.1 cowplot_1.1.3 grid_4.4.1
+ [94] ape_5.8 tidyr_1.3.1 AnnotationDbi_1.66.0
+ [97] colorspace_2.1-0 nlme_3.1-164 patchwork_1.2.0
+[100] GenomeInfoDbData_1.2.12
+ [ reached getOption("max.print") -- omitted 20 entries ]
diff --git a/pathway-analysis/03-gene_set_variation_analysis-live.Rmd b/pathway-analysis/03-gene_set_variation_analysis-live.Rmd
index 13369210..84aa07d3 100644
--- a/pathway-analysis/03-gene_set_variation_analysis-live.Rmd
+++ b/pathway-analysis/03-gene_set_variation_analysis-live.Rmd
@@ -1,11 +1,11 @@
---
title: "Pathway analysis: Gene Set Variation Analysis (GSVA)"
-output:
+output:
html_notebook:
toc: true
toc_float: true
author: CCDL for ALSF
-date: 2020
+date: 2024
---
## Objectives
@@ -42,8 +42,6 @@ Note that these scores will depend on the samples included in the dataset when y
### Libraries
```{r libraries}
-# Pipes
-library(magrittr)
# Gene Set Variation Analysis
library(GSVA)
```
@@ -53,12 +51,12 @@ library(GSVA)
#### Directories
```{r directories}
-# We have some medulloblastoma data from the OpenPBTA project that we've
+# We have some medulloblastoma data from the OpenPBTA project that we've
# prepared ahead of time
input_dir <- file.path("data", "open-pbta")
# Create a directory specifically for the results using this dataset
-output_dir <- file.path("results", "open-pbta")
+output_dir <- file.path("results", "open-pbta")
if (!dir.exists(output_dir)) {
dir.create(output_dir, recursive = TRUE)
}
@@ -139,7 +137,7 @@ For GSVA, we need a matrix.
You may notice that GSVA has some commonalities with GSEA.
Rather than ranking genes based on some statistic _we_ selected ahead of time, GSVA fits a model and ranks genes based on their expression level relative to the sample distribution.
This is a way of asking if a gene _i_ is highly or lowly expressed in a sample _j_ in the context of this experiment and ranking accordingly ([Hänzelmann _et al._ 2013](https://doi.org/10.1186/1471-2105-14-7)).
-The pathway-level score calculated is a way of asking how genes _within_ a gene set vary as compared to genes that are _outside_ of that gene set ([Malhotra. 2018](https://towardsdatascience.com/decoding-gene-set-variation-analysis-8193a0cfda3)).
+The pathway-level score calculated is a way of asking how genes _within_ a gene set vary as compared to genes that are _outside_ of that gene set ([Malhotra. 2018](https://towardsdatascience.com/decoding-gene-set-variation-analysis-8193a0cfda3)).
(This is sometimes called a competitive test in gene set enrichment literature.)
The intuition here is that we will get pathway-level scores for each sample that indicate if genes in a pathway vary concordantly in one direction (overexpressed or underexpressed relative to the overall population) ([Hänzelmann _et al._ 2013](https://doi.org/10.1186/1471-2105-14-7)).
@@ -147,21 +145,30 @@ The output is a gene set by sample matrix of GSVA scores.
### Perform GSVA
+The main work for GSVA is done by the `gsva()` function, which can actually do a variety of different enrichment score calculations.
+To specify that we want the algorithm used by [Hänzelmann _et al._ (2013)](https://doi.org/10.1186/1471-2105-14-7), we will pass as the first argument a call to the `gsvaParam()` function, which is where we will put our data, gene sets, and other parameters specific to that algorithm.
+
```{r run_gsva}
-gsva_results <- gsva(rnaseq_mat,
- hallmarks_list,
- method = "gsva",
- # Appropriate for our transformed data on log2-like scale
- kcdf = "Gaussian",
- # Minimum gene set size
- min.sz = 15,
- # Maximum gene set size
- max.sz = 500,
- # Compute Gaussian-distributed scores
- mx.diff = TRUE)
+gsva_results <- gsva(
+ gsvaParam(
+ exprData = rnaseq_mat,
+ geneSets = hallmarks_list,
+ # Minimum gene set size
+ minSize = 15,
+ # Maximum gene set size
+ maxSize = 500,
+ # Kernel for estimation
+ # Gaussian for our transformed data on log2-like scale
+ kcdf = "Gaussian",
+ # enrichment score is the difference between largest positive and negative
+ maxDiff = TRUE
+ ),
+ # Use 4 cores for multiprocessing
+ BPPARAM = BiocParallel::MulticoreParam(4)
+)
```
-**Note: the `gsva()` documentation says we can use `kcdf = "Gaussian"` if we had RNA-seq log-CPMs, log-RPKMs or log-TPMs, but we would use `kcdf = "Poisson"` on integer counts.**
+**Note: the `gsvaParam()` documentation says we can use `kcdf = "Gaussian"` if we had RNA-seq log-CPMs, log-RPKMs or log-TPMs, but we would use `kcdf = "Poisson"` on integer counts.**
```{r gsva_peek}
# Let's explore what the output of gsva() looks like
@@ -169,7 +176,7 @@ gsva_results[1:5, 1:5]
```
### A note on gene set size
-
+
Often the scores of gene set enrichment methods are not comparable between gene sets of different sizes.
Let's do an experiment using randomly generated gene sets to explore this idea a bit more.
@@ -184,7 +191,7 @@ all_genes <- rownames(rnaseq_mat)
set.seed(2020)
```
-Our minimum gene set size earlier was 15 genes and our maximum gene set size was 500 genes.
+Our minimum gene set size earlier was 15 genes and our maximum gene set size was 500 genes.
We'll use the same minimum and maximum values for our random gene sets and some values in between.
```{r gene_set_sizes}
@@ -198,7 +205,7 @@ For each gene set size, we will generate 100 random gene sets.
# Set number of replicates
nreps <- 100
# Generate 100 random gene sets of each size
-random_gene_sets <- rep(gene_set_size, nreps) %>% # Repeat gene sizes so we run `nreps` times
+random_gene_sets <- rep(gene_set_size, nreps) |> # Repeat gene sizes so we run `nreps` times
purrr::map(
# Sample the vector of all genes, choosing the number of items specified
# in the element of gene set size
@@ -210,35 +217,28 @@ random_gene_sets <- rep(gene_set_size, nreps) %>% # Repeat gene sizes so we run
The Hallmarks list we used earlier stored the gene set names as the name of the list, so let's add names to our random gene sets that indicate what size they are and so `gsva()` doesn't get upset.
```{r name_random_gene_sets}
-# We will include the size of the gene set in the gene set name
+# We will include the size of the gene set in the gene set name
# Start by taking the length of each pathway and appending "pathway_" to that
# number
-lengths_vector <- random_gene_sets %>%
+lengths_vector <- random_gene_sets |>
# Get the length of each gene set (number of genes)
- purrr::map(~ length(.x)) %>%
+ purrr::map(~ length(.x)) |>
# Make it "pathway_"
- purrr::map(~ paste0("pathway_", .x)) %>%
+ purrr::map(~ paste0("pathway_", .x)) |>
# Return a vector
purrr::flatten_chr()
# Add the names in lengths_vector to the list - "pathway_"
-random_gene_sets <- random_gene_sets %>%
+random_gene_sets <- random_gene_sets |>
# make.names() appends a "version" if something is not unique
purrr::set_names(nm = make.names(lengths_vector, unique = TRUE))
```
-
+
Run GSVA on our dataset with the same parameters as before, but now with random gene sets.
```{r random_gsva, live = TRUE}
- # Appropriate for our transformed data on
- # log2-like scale
-
- # Minimum gene set size
-
- # Maximum gene set size
-
- # Compute Gaussian-distributed scores
+ # Use 4 cores for multiprocessing
```
@@ -247,20 +247,22 @@ First we need to get this data in an appropriate format for `ggplot2`.
```{r longer_random_gsva}
# The random results are a matrix
-random_long_df <- random_gsva_results %>%
- data.frame() %>%
+random_long_df <- random_gsva_results |>
+ data.frame() |>
# Gene set names are rownames
- tibble::rownames_to_column("gene_set") %>%
+ tibble::rownames_to_column("gene_set") |>
# Get into long format
- tidyr::pivot_longer(cols = -gene_set,
- names_to = "Kids_First_Biospecimen_ID",
- values_to = "gsva_score") %>%
+ tidyr::pivot_longer(
+ cols = -gene_set,
+ names_to = "Kids_First_Biospecimen_ID",
+ values_to = "gsva_score"
+ ) |>
# Remove the .version added by make.names()
- dplyr::mutate(gene_set = stringr::str_remove(gene_set, "\\..*")) %>%
+ dplyr::mutate(gene_set = stringr::str_remove(gene_set, "\\..*")) |>
# Add a column that keeps track of the gene set size
- dplyr::mutate(gene_set_size = stringr::word(gene_set, 2, sep = "_")) %>%
+ dplyr::mutate(gene_set_size = stringr::word(gene_set, 2, sep = "_")) |>
# We want to plot smallest no. genes -> largest no. genes
- dplyr::mutate(gene_set_size = factor(gene_set_size,
+ dplyr::mutate(gene_set_size = factor(gene_set_size,
levels = c(15, 25, 50, 100, 250, 500)))
```
@@ -269,8 +271,8 @@ Let's make a violin plot so we can look at the distribution of scores by gene se
```{r random_violin}
# Violin plot comparing GSVA scores of different random gene set sizes
-random_long_df %>%
- ggplot2::ggplot(ggplot2::aes(x = gene_set_size,
+random_long_df |>
+ ggplot2::ggplot(ggplot2::aes(x = gene_set_size,
y = gsva_score)) +
# Make a violin plot that's a pretty blue!
ggplot2::geom_violin(fill = "#99CCFF", alpha = 0.5) +
@@ -297,7 +299,7 @@ How might you use this information to inform your interpretation of GSVA scores?
### How can you use these scores?
If you did have groups information for your samples, you could test for pathway scores differences between groups.
-Here's [an example](https://htmlpreview.github.io/?https://github.com/AlexsLemonade/OpenPBTA-analysis/blob/9b44bf1c186b3126b16dbe5b87756b3eae3feec2/analyses/gene-set-enrichment-analysis/02-model-gsea.nb.html) of that from the OpenPBTA project itself!
+Here's [an example](https://htmlpreview.github.io/?https://github.com/AlexsLemonade/OpenPBTA-analysis/blob/9b44bf1c186b3126b16dbe5b87756b3eae3feec2/analyses/gene-set-enrichment-analysis/02-model-gsea.nb.html) of that from the OpenPBTA project itself!
You can also visualize this matrix in a heatmap.
Here's a figure from the OpenPBTA project, where the middle panel is a heatmap of GSVA scores that were significantly different between histologies.
@@ -309,9 +311,9 @@ Here's a figure from the OpenPBTA project, where the middle panel is a heatmap o
### Write results to file
```{r write_gsva_results}
-gsva_results %>%
- as.data.frame() %>%
- tibble::rownames_to_column("pathway") %>%
+gsva_results |>
+ as.data.frame() |>
+ tibble::rownames_to_column("pathway") |>
readr::write_tsv(file = gsva_results_file)
```
diff --git a/pathway-analysis/03-gene_set_variation_analysis.nb.html b/pathway-analysis/03-gene_set_variation_analysis.nb.html
index c3733022..eefefc0f 100644
--- a/pathway-analysis/03-gene_set_variation_analysis.nb.html
+++ b/pathway-analysis/03-gene_set_variation_analysis.nb.html
@@ -11,43 +11,2661 @@
-
+
Pathway analysis: Gene Set Variation Analysis (GSVA)
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+code{white-space: pre-wrap;}
+span.smallcaps{font-variant: small-caps;}
+span.underline{text-decoration: underline;}
+div.column{display: inline-block; vertical-align: top; width: 50%;}
+div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
+ul.task-list{list-style: none;}
+
-