diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d3faf9859..0a8ba1231d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,10 +20,6 @@ jobs: name: Check python packages command: ./scripts/run_in_ci.sh bash scripts/check-python.sh - - run: - name: High level histology grouping for plot labels - command: ./scripts/run_in_ci.sh Rscript -e "rmarkdown::render('figures/mapping-histology-labels.Rmd', clean = TRUE)" - - run: name: Sample Distribution Analyses command: ./scripts/run_in_ci.sh bash "analyses/sample-distribution-analysis/run-sample-distribution.sh" diff --git a/figures/mapping-histology-labels.Rmd b/figures/mapping-histology-labels.Rmd index ac24d02a36..2faa73c6b8 100644 --- a/figures/mapping-histology-labels.Rmd +++ b/figures/mapping-histology-labels.Rmd @@ -1,52 +1,42 @@ --- -title: "Mapping histology labels for plots" +title: "Create a minimal palette for displaying multiple disease labels" output: html_notebook: toc: true toc_float: true -author: Candace Savonen for ALSF - CCDL +author: Candace Savonen, Krutika Gaonkar, and Jaclyn Taroni date: 2021 --- -# Purpose: +## Purpose -The histology label variables included in `pbta-histologies.tsv` from data releases are not always useful for visualizing the full set of biospecimens due to the large number of different values. -Having too many different possible values makes the colors harder to distinguish. -In addition, there are some groups that are represented by only a very few samples; giving such groups a distinct color may be counterproductive. +There are multiple "disease labels" in the `pbta-histologies.tsv` file, including (from most broad to most narrow) `broad_histology`, `cancer_group`, and `harmonized_diagnosis`. +For context, it is helpful to note that an individual `cancer_group` will be nested under a single `broad_histology` and that `cancer_group` is a shorter form of `harmonized_diagnosis` with the following edits: -The goal of this notebook is to use the currently existing `broad_histology` groups from `pbta-histologies.tsv`, to form 10-15 "high level histology" group labels that can used for plotting purposes. - -## The output table - -The output of this notebook is a TSV file: `palettes/histology_label_color_table.tsv` that contains the following fields: - -**Copied from `pbta-histologies.tsv`**: -- `Kids_First_Biospecimen_ID` (from `pbta-histologies.tsv`) -- All the original histology label variables (`broad_histology`, `short_histology`, etc.) - -**Created in this notebook**: -- `display_group` - the high-level histology labels that should be used for plotting -- `hex_codes` the direct colors that correspond to display_groups -- `cancer_group_hex_codes` the direct colors that correspond to cancer_groups - -With this info, `histology_label_color_table.tsv` can be used by all plots and figures that summarize high level data while displaying histology information. +- Other, Benign tumor and Dysplasia/Gliosis, Dysplasia/Gliosis-Glial-neuronal tumor NOS removed from `cancer_group` +- Neurofibroma/Plexiform;Other updated to Neurofibroma/Plexiform +- Non-germinomatous germ cell tumor;Teratoma updated to Teratoma +- Anaplastic (malignant) meningioma, Meningothelial meningioma and Clear cell meningioma updated to Meningioma +- Embryonal Tumor with Multilayered Rosettes updated to Embryonal tumor with multilayer rosettes -# How `display_group` is made: +It is often useful to use color to indicate disease label in a plot where multiple groups are visualized when we can not rely particularly heavily on labels (e.g., scatter plots). +Unfortunately, there are too many potential labels for us to generate an effective color palette (e.g., of sufficiently distinct colors). +In addition, some groupings will contain very few samples. -Here's how `broad-histology` groups are [combined into the higher-level groupings of `display_group`](#declare-new-equivalent-groups). +The purpose of this notebook is to create color palettes for the following: -1) "Lymphoma", "Melanocytic tumor", "Other tumor", "Metastatic tumors", "Non-CNS tumor" are combined into a `Other tumor` in `display_group`. +* `broad_histology` values, where a `broad_histology` contains at least one `cancer_group` with n >= 10 +* `cancer_group` values with n >= 10 -2) `Benign tumor` and `Non-tumor` biospecimens are combined into a `Benign` group. +**Note: This is tied to `release-v21-20210820`.** -3) `Other astrocytic tumor` biospecimens are combined into the existing `Low-grade astrocytic tumor`. These biospecimens in `other astrocytic tumors` were low-grade SEGA tumors. +### Background -4) Anything not in the above categories gets its `broad_histology` label carried over. +You may find [#1174](https://github.com/AlexsLemonade/OpenPBTA-analysis/issues/1174) to be helpful context. -# Usage +## Usage -This notebook can be run via the command line from the top directory of the -repository as follows: +This notebook can be run via the command line from the top directory of the repository as follows: ``` Rscript -e "rmarkdown::render('figures/mapping-histology-labels.Rmd', @@ -56,284 +46,346 @@ Rscript -e "rmarkdown::render('figures/mapping-histology-labels.Rmd', ## Set Up ```{r} -# Magrittr pipe -`%>%` <- dplyr::`%>%` +library(tidyverse) +library(RColorBrewer) ``` ### Directories and Files ```{r} # Path to input directory -input_dir <- file.path("..", "data") +input_dir <- file.path("..", "data", "release-v21-20210820") output_dir <- "palettes" ``` -# Read in metadata - -Which variables are we keeping for this table? - -```{r} -histology_variables <- - c("integrated_diagnosis", - "Notes", - "harmonized_diagnosis", - "broad_histology", - "short_histology", - "cancer_group") -``` +## Read in metadata -Let's read in the current release's `pbta-histologies.tsv` file. +Let's read in the `pbta-histologies.tsv` file from `release-v21-20210820`. ```{r} -metadata <- +histologies_df <- readr::read_tsv(file.path(input_dir, "pbta-histologies.tsv"), guess_max = 10000) ``` -Now we'll select histology variables we mentioned above and so capitalization differences don't get in the way with this process, we will change everything to lower case for now. - -```{r} -working_metadata <- metadata %>% - dplyr::select(Kids_First_Biospecimen_ID, sample_type, histology_variables) %>% - dplyr::mutate(broad_histology_lower = tolower(broad_histology)) -``` - -# Take a look at how many biospecimens per `broad_histology` group +## Identify values to include in palettes -Let's summarize `broad_histology`. -Because the `Normal` samples don't have histologies, we'll look at just the `Tumor` samples at for this summary. +We will use `cancer_group` with n >= 10 to guide what values to include in both our `cancer_group` and `broad_histology` palettes. ```{r} -broad_summary <- working_metadata %>% - dplyr::filter(sample_type == "Tumor") %>% - dplyr::count(broad_histology_lower) %>% - dplyr::arrange(n) +included_labels_df <- histologies_df %>% + # Exclude normal samples + filter(sample_type == "Tumor") %>% + # Filter to unique sample--"disease label" pairs + select(sample_id, + broad_histology, + cancer_group) %>% + distinct() %>% + # Count samples (e.g., sample_id) + group_by(broad_histology, cancer_group) %>% + tally() %>% + # Add a column called included which is a logical that can be used as + # a sample size filter & also to drop the NA values + filter(n >= 10, + !is.na(cancer_group)) + +included_labels_df ``` -Let's print out the summary. +So the unique values for `broad_histology` and `cancer_group` above are what we need to take into account for our palette. -```{r} -broad_summary %>% - knitr::kable() -``` - -There's handful of very small groups (many are n = 2). +## Create palettes -## Declare new equivalent groups +Outside of this notebook, we've done quite a bit of work to identify suitable palettes using http://phrogz.net/css/distinct-colors.html as a reference/starting point. +Check out the discussion on [#1174](https://github.com/AlexsLemonade/OpenPBTA-analysis/issues/1174)! -These groups we'll combine into a non-CNS/other tumor group. +### `broad_histology` ```{r} -other_tumor <- c("lymphoma", "melanocytic tumor", "other tumor", "metastatic tumors", "non-cns tumor") -``` - -These groups we'll combine as a benign. +broad_histology_df <- data.frame( + broad_histology = c("Benign tumor", + "Diffuse astrocytic and oligodendroglial tumor", + "Embryonal tumor", + "Ependymal tumor", + "Germ cell tumor", + "Low-grade astrocytic tumor", + "Meningioma", + "Mesenchymal non-meningothelial tumor", + "Neuronal and mixed neuronal-glial tumor", + "Tumor of cranial and paraspinal nerves", + "Tumors of sellar region"), + broad_histology_hex = c("#590024", + "#ff80e5", + "#220040", + "#2200ff", + "#0074d9", + "#8f8fbf", + "#2db398", + "#7fbf00", + "#685815", + "#ffaa00", + "#b2502d"), + stringsAsFactors = FALSE +) -```{r} -benign <- c("benign tumor", "non-tumor") +# value for "other" histologies +broad_histology_other_hex <- "#808080" ``` -Add in the `Other astrocytic tumor` in with the LGAT group. +Now to create a legend with `legend()` (h/t [this StackOverflow answer](https://stackoverflow.com/questions/48966645/how-can-i-create-a-legend-without-a-plot-in-r/48966924)) ```{r} -lgat <- c("other astrocytic tumor", "low-grade astrocytic tumor") +plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", + xlim = 0:1, ylim = 0:1) +legend("topleft", + legend = c(broad_histology_df$broad_histology, "Other"), + col = c(broad_histology_df$broad_histology_hex, + broad_histology_other_hex), + pch = 15, pt.cex = 2, cex = 1, bty = "n") +mtext("Broad Histology", at = 0.135, cex = 1.5) ``` -# Make new `display_group` +### `cancer_group` -```{r} -histology_table <- working_metadata %>% - dplyr::mutate( - # NAs are really Normals - display_group = tidyr::replace_na(broad_histology_lower, "normal"), - # Now do the group combining - display_group = forcats::fct_collapse(display_group, - "low-grade astrocytic tumor" = lgat, - "other tumor" = other_tumor, - "benign" = benign - ), - # Put this as a character for later handling - display_group = as.character(display_group) - ) -``` +There are 17 `cancer_group` values that we need to account for. +These are best used in conjunction with _labels_ in figures, but are intended to allow readers to "track" labels _across figures_. -Print out the number of `display_group` (including `normal`)! +Where there's a 1:1 mapping between `broad_histology` and `cancer_group`, the hex codes will be the same. ```{r} -display_group_df <- histology_table %>% - dplyr::count(display_group) %>% - dplyr::arrange(n) - -knitr::kable(display_group_df) +cancer_group_df <- data.frame( + cancer_group = c("Choroid plexus papilloma", + "Diffuse intrinsic pontine glioma", + "Diffuse midline glioma", + "High-grade glioma astrocytoma", + "Atypical Teratoid Rhabdoid Tumor", + "CNS Embryonal tumor", + "Medulloblastoma", + "Ependymoma", + "Teratoma", + "Ganglioglioma", + "Low-grade glioma astrocytoma", + "Meningioma", + "Ewing sarcoma", + "Dysembryoplastic neuroepithelial tumor", + "Neurofibroma Plexiform", + "Schwannoma", + "Craniopharyngioma"), + cancer_group_hex = c("#4d2635", + "#bf0099", + "#ff40d9", + "#ffccf5", + "#4d0d85", + "#b08ccf", + "#a340ff", + "#2200ff", + "#058aff", + "#8c8cff", + "#000080", + "#2db398", + "#9fbf60", + "#614e01", + "#e6ac39", + "#ab7200", + "#b33000"), + stringsAsFactors = FALSE +) +# Value for "other" groups +cancer_group_other_hex <- "#b5b5b5" ``` -Make this notebook stop if there are more than 16 histology groups + `Normal`. +And again, we'll create a legend with `legend()` ```{r} -if (nrow(display_group_df) > 18) { - stop("There are more than 18 categories in `display_group`. We may want to re-evaluate the high-level histology groupings") -} +plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", + xlim = 0:1, ylim = 0:1) +legend("topleft", + legend = c(cancer_group_df$cancer_group, "Other"), + col = c(cancer_group_df$cancer_group_hex, cancer_group_other_hex), + pch = 15, pt.cex = 1.5, cex = 0.75, bty = "n") +mtext("Cancer Group", at = 0.0625, cex = 1) ``` -# Make `display_order` +### Output -Get ranks in order of big to small and make them into a new column in `display_group_df`. -We will always want the "normal", "benign", "other_tumor" groups to come last so we will push then to the end of the factor order. +We can create a data frame that contains both palettes with a series of left joins, where we will then fill the NA values with a single (gray) hex code per column (`r broad_histology_other_hex` for `broad_histology`, `r cancer_group_other_hex` for `cancer_group`.) ```{r} -display_order_df <- display_group_df %>% - dplyr::mutate(display_group = forcats::fct_reorder(display_group, n, .desc = TRUE) %>% - forcats::fct_relevel("benign", "other tumor", "normal", after = Inf), - display_order = as.numeric(display_group)) # save the factor order for text table export +palette_df <- histologies_df %>% + # Exclude normal samples + filter(sample_type == "Tumor") %>% + # Filter to unique broad histology--cancer group pairs + select(broad_histology, + cancer_group) %>% + distinct() %>% + # Add broad histology palette + left_join(broad_histology_df, by = "broad_histology") %>% + # Add cancer group palette + left_join(cancer_group_df, by = "cancer_group") %>% + # Fill all other values with gray colors + replace_na(list(broad_histology_hex = broad_histology_other_hex, + cancer_group_hex = cancer_group_other_hex)) %>% + # The exception being - if cancer_group == NA, so should cancer_group_hex! + mutate(cancer_group_hex = if_else(is.na(cancer_group), + NA_character_, + cancer_group_hex)) %>% + # Sort by broad_histology for easy browsing + arrange(broad_histology) ``` -# Make `cancer_group_order` +And now let's take a look! -`cancer_group` is a shorter form of `harmonized_diagnosis` with the following edits: -- Removed Other, Benign tumor and Dysplasia/Gliosis,Dysplasia/Gliosis-Glial-neuronal tumor NOS removed from `cancer_group` -- Neurofibroma/Plexiform;Other updated to Neurofibroma/Plexiform -- Non-germinomatous germ cell tumor;Teratoma updated to Teratoma -- Anaplastic (malignant) meningioma, Meningothelial meningioma and Clear cell meningioma updated to Meningioma -- Embryonal Tumor with Multilayered Rosettes updated to Embryonal tumor with multilayer rosettes - -Get ranks in order of big to small and make them into a new dataframe `cancer_group_order_df`. - ```{r} -cancer_group_order_df <- histology_table %>% - dplyr::count(cancer_group,name = "cancer_group_n") %>% - dplyr::mutate( - cancer_group = forcats::fct_reorder(cancer_group, cancer_group_n, .desc = TRUE), - cancer_group_order = as.numeric(cancer_group)) # save the factor order for text table export +palette_df ``` +### Add display names for convenience -Add on the `display_order` column using `inner_join` +When multiple values are using the same color, it can be helpful to have a separate value for the legend, e.g., for all `#808080` broad histologies, we may want to display `Other`. +We'll add a couple columns for legend-making convenience. ```{r} -histology_table <- histology_table %>% - # Join on the display orders - dplyr::inner_join(display_order_df, by = "display_group") %>% - # Join on the cancer_group orders - dplyr::inner_join(cancer_group_order_df, by = "cancer_group") +palette_df <- palette_df %>% + mutate(broad_histology_display = if_else(broad_histology_hex == broad_histology_other_hex, + "Other", + broad_histology), + cancer_group_display = if_else(cancer_group_hex == cancer_group_other_hex, + "Other", + cancer_group)) ``` -# Add hex codes for display_group and cancer_group +### Add `broad_histology_order` -These hex codes were retrieved from http://phrogz.net/css/distinct-colors.html with the settings on default for 18 colors. +Previously, we had a concept known as `display_order` where we ordered categories based on their number of samples (from large to small). +Now that we've dropped `display_group`, let's apply this same concept to `broad_histology`. ```{r} -color_palette_display <- - c("#ff0000", "#cc0000", "#995200", "#bfb300", "#fffbbf", - "#2e7300", "#00e65c", "#00ffee", "#103d40", "#0085a6", - "#003380", "#4073ff", "#737899", "#70008c", "#f2b6ee", - "#ff40bf", "#8c0038", "#330d12" -) - -color_palette_cancer_group <- - c("#ff0000", "#f20000", "#997373", "#403030", "#330700", - "#ff9180", "#591800", "#b2502d", "#cca799", "#ff6600", - "#ffb380", "#7f5940", "#cc6d00", "#331b00", "#ccb499", - "#ffaa00", "#996600", "#594316", "#ffd580", "#ffee00", - "#998f00", "#999673", "#303300", "#fbffbf", "#ccff00", - "#494d39", "#b5d96c", "#6a8040", "#66ff00", "#42a600", - "#bfffbf", "#003307", "#00661b", "#00ff88", "#86b39e", - "#00b377", "#006652", "#00ffee", "#00a7b3", "#bffbff", - "#567173", "#00ccff", "#003d4d", "#00aaff", "#267399", - "#0088ff", "#0042a6", "#001a40", "#bfd9ff", "#0044ff", - "#394973", "#000e66", "#bfbfff", "#9180ff", "#5800a6", - "#754d99", "#aa00ff", "#3a3040", "#aa86b3", "#530059", - "#ff00ee", "#a60085", "#330022", "#ff80d5", "#ff0088", - "#804062", "#a60042", "#590024", "#ffbfd9", "#ff0044", - "#990014", "#ff8091" -) - +broad_histology_order_df <- histologies_df %>% + # Exclude normal samples + filter(sample_type == "Tumor", + # Only count histologies that we'll have a hex code for + broad_histology %in% included_labels_df$broad_histology) %>% + # Filter to unique sample--broad_histology pairs + select(sample_id, + broad_histology) %>% + distinct() %>% + # Count samples within a broad histology + count(broad_histology) %>% + # Add Other placeholder + bind_rows(data.frame(broad_histology = "Other", + n = 0, + stringsAsFactors = FALSE)) %>% + # Reorder based on sample size except Benign tumor and Other should come last + # And then add numeric column with the order + mutate(broad_histology = forcats::fct_reorder(broad_histology, + n, + .desc = TRUE) %>% + forcats::fct_relevel("Benign tumor", + "Other", + after = Inf), + broad_histology_order = as.numeric(broad_histology)) %>% + # No longer require the sample size + select(-n) + +broad_histology_order_df ``` -Declare how many colors we need. +And now we're ready to add this to the palette data frame. ```{r} -n_colors_display <- nrow(display_group_df) -n_colors_cancer_group <- nrow(cancer_group_order_df) +palette_df <- palette_df %>% + left_join(broad_histology_order_df, + by = c("broad_histology_display" = "broad_histology")) ``` -Make a named list color key where histologies are the names. +### Add `oncoprint_group` and `oncoprint_hex` -```{r} -# Set seed so the colors are consistent upon re-run -set.seed(2021) +For most plots that make use of the `cancer_group` palette, such as a box or violin plot, we will rely heavily on labels and therefore using the gray hex code for multiple groups will not be a problem. -# Sample from the 18 colors for display_group -subset_colors_display <- sample(color_palette_display, n_colors_display) -names(subset_colors_display) <- display_order_df$display_group +We will have four panels of individual oncoprints, where many `broad_histology` values will get grouped together into the `Other CNS` panel which you can see [here](https://github.com/AlexsLemonade/OpenPBTA-analysis/blob/d31c927a27813ec0b8032fbe768002f31723636f/analyses/oncoprint-landscape/02-plot-oncoprint.R#L181). +We can move this information into our palette data frame. -# Sample from the 62 colors for cancer_group -subset_colors_cancer_group <- sample(color_palette_cancer_group, n_colors_cancer_group) -names(subset_colors_cancer_group) <- cancer_group_order_df$cancer_group -``` - -Remove from subset_colors_cancer_group ```{r} -# We will assign a gray color for NA below -subset_colors_cancer_group <- subset_colors_cancer_group[!is.na(names(subset_colors_cancer_group))] -``` - -We want `Other tumor` and the `Benign` in display_group to both always be gray. +# Taken from the current plot oncoprint script as of the writing of this +# See permalink above +other_cns_broad_histologies <- c( + "Ependymal tumor", + "Tumors of sellar region", + "Neuronal and mixed neuronal-glial tumor", + "Tumor of cranial and paraspinal nerves", + "Meningioma", + "Mesenchymal non-meningothelial tumor", + "Germ cell tumor", + "Choroid plexus tumor", + "Histiocytic tumor", + "Tumor of pineal region", + "Metastatic tumors", + "Other astrocytic tumor", + "Lymphoma", + "Melanocytic tumor", + "Other tumor" +) -```{r} -subset_colors_display[names(subset_colors_display) == 'other tumor'] <- "#808080" -subset_colors_display[names(subset_colors_display) == 'benign'] <- "#D3D3D3" +palette_df <- palette_df %>% + mutate(oncoprint_group = case_when( + broad_histology %in% other_cns_broad_histologies ~ "Other CNS", + broad_histology %in% c( + "Low-grade astrocytic tumor", + "Embryonal tumor", + "Diffuse astrocytic and oligodendroglial tumor" + ) ~ broad_histology, + TRUE ~ NA_character_ + )) ``` -Normal biospecimens should not get plotted in display_group, so we will put their hex code as black. +For cancer groups that do not get their own hex code for display (i.e., due to small sample sizes), we'll use a selection of grey colors as a palette and rely heavily on the ordering of the OncoPrint legend. +Unfortunately there are over 20 "Other CNS" cancer groups that meet this criterion, so it is not feasible to have a color for each of them and they will not be included in the "Other CNS" OncoPrint. ```{r} -subset_colors_display[names(subset_colors_display) == 'normal'] <- "#000000" -``` +greys_df <- palette_df %>% + filter(cancer_group_display == "Other", + !is.na(oncoprint_group), + oncoprint_group != "Other CNS") -Use `pie` function to preview what display_group these look like. - -```{r} -pie(rep(1, n_colors_display), - col = subset_colors_display, - labels = names(subset_colors_display)) +# Sample the greys sequential palette from color brewer +set.seed(2021) +greys_df <- greys_df %>% + mutate(oncoprint_hex = sample(brewer.pal(nrow(greys_df), "Greys"))) ``` -Use `pie` function to preview what cancer_group these look like. - -```{r} -pie(rep(1, n_colors_cancer_group), - col = subset_colors_cancer_group, - labels = names(subset_colors_cancer_group)) -``` +Add `oncoprint_hex` & `oncoprint_include`, the latter will be `FALSE` when the former is `NA`. +Add `oncoprint_hex` for all other cancer groups and `oncoprint_include`. +The latter will be FALSE when the former is NA. -Add the hex codes for display_group and cancer_group to the `histology_table`. -Add gray color if cancer_group==NA ```{r} -histology_table <- histology_table %>% - # We don't need this anymore - dplyr::select(-broad_histology_lower) %>% - # Add the hex_codes - dplyr::mutate(hex_codes = dplyr::recode(display_group, !!!subset_colors_display), - cancer_group_hex_codes = dplyr::recode(cancer_group, !!!subset_colors_cancer_group), - # if cancer_group is NA for tumor sample add gray color - cancer_group_hex_codes = dplyr::if_else(is.na(cancer_group_hex_codes) & sample_type=="Tumor" - ,"#808080", - cancer_group_hex_codes) - ) %>% - # Restore capitalization so its pretty for labeling - dplyr::mutate(display_group = stringr::str_to_sentence(display_group), - # Deal with CNS exception - display_group = stringr::str_replace(display_group, "cns", "CNS") - ) +palette_df <- palette_df %>% + left_join(greys_df) %>% + # When there's an oncoprint group and a specific cancer group display color, + # use the cancer group display color as the oncoprint color + mutate(oncoprint_hex = if_else( + cancer_group_hex != cancer_group_other_hex & !is.na(oncoprint_group), + cancer_group_hex, + oncoprint_hex + ), + # Only when there's a specific oncoprint color -- even if that is a grey + # selected only for the oncoprint -- will a cancer group be included in the + # oncoprint + oncoprint_include = if_else( + is.na(oncoprint_hex), + FALSE, + TRUE + )) ``` -## Save to TSV +### Save to TSV ```{r} -readr::write_tsv(histology_table, file.path(output_dir, "histology_label_color_table.tsv")) +readr::write_tsv(palette_df, + file.path(output_dir, + "broad_histology_cancer_group_palette.tsv")) ``` -# Session Info +## Session Info ```{r} sessionInfo() diff --git a/figures/mapping-histology-labels.nb.html b/figures/mapping-histology-labels.nb.html index d8d06fdd0d..6a04b14e0a 100644 --- a/figures/mapping-histology-labels.nb.html +++ b/figures/mapping-histology-labels.nb.html @@ -9,11 +9,11 @@ - + -Mapping histology labels for plots +Create a minimal palette for displaying multiple disease labels + + +

So the unique values for broad_histology and cancer_group above are what we need to take into account for our palette.

-
-

Take a look at how many biospecimens per broad_histology group

-

Let’s summarize broad_histology. Because the Normal samples don’t have histologies, we’ll look at just the Tumor samples at for this summary.

+
+

Create palettes

+

Outside of this notebook, we’ve done quite a bit of work to identify suitable palettes using http://phrogz.net/css/distinct-colors.html as a reference/starting point. Check out the discussion on #1174!

+
+

broad_histology

- -
broad_summary <- working_metadata %>% 
-  dplyr::filter(sample_type == "Tumor") %>%
-  dplyr::count(broad_histology_lower) %>% 
-  dplyr::arrange(n) 
+ +
broad_histology_df <- data.frame(
+  broad_histology = c("Benign tumor",
+                      "Diffuse astrocytic and oligodendroglial tumor",
+                      "Embryonal tumor",
+                      "Ependymal tumor",
+                      "Germ cell tumor",
+                      "Low-grade astrocytic tumor",
+                      "Meningioma",
+                      "Mesenchymal non-meningothelial tumor",
+                      "Neuronal and mixed neuronal-glial tumor",
+                      "Tumor of cranial and paraspinal nerves",
+                      "Tumors of sellar region"), 
+  broad_histology_hex = c("#590024",
+                          "#ff80e5",
+                          "#220040",
+                          "#2200ff",
+                          "#0074d9",
+                          "#8f8fbf",
+                          "#2db398",
+                          "#7fbf00",
+                          "#685815",
+                          "#ffaa00",
+                          "#b2502d"),
+  stringsAsFactors = FALSE
+)
+
+# value for "other" histologies
+broad_histology_other_hex <- "#808080"
-

Let’s print out the summary.

+

Now to create a legend with legend() (h/t this StackOverflow answer)

- -
broad_summary %>% 
-  knitr::kable()
+ +
plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", 
+     xlim = 0:1, ylim = 0:1)
+legend("topleft", 
+       legend = c(broad_histology_df$broad_histology, "Other"),
+       col = c(broad_histology_df$broad_histology_hex, 
+               broad_histology_other_hex),
+       pch = 15, pt.cex = 2, cex = 1, bty = "n")
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
broad_histology_lowern
lymphoma2
melanocytic tumor2
non-cns tumor2
other tumor2
non-tumor6
choroid plexus tumor9
tumor of pineal region10
chordoma12
metastatic tumors13
histiocytic tumor14
germ cell tumor27
pre-cancerous lesion27
mesenchymal non-meningothelial tumor49
meningioma59
benign tumor70
tumors of sellar region73
neuronal and mixed neuronal-glial tumor83
tumor of cranial and paraspinal nerves83
ependymal tumor174
embryonal tumor333
diffuse astrocytic and oligodendroglial tumor370
low-grade astrocytic tumor587
- - -

There’s handful of very small groups (many are n = 2).

-
-

Declare new equivalent groups

-

These groups we’ll combine into a non-CNS/other tumor group.

- - - -
other_tumor <- c("lymphoma", "melanocytic tumor", "other tumor", "metastatic tumors", "non-cns tumor")
+ +
mtext("Broad Histology", at = 0.135, cex = 1.5)
+ +

+ -

These groups we’ll combine as a benign.

+
+
+

cancer_group

+

There are 17 cancer_group values that we need to account for. These are best used in conjunction with labels in figures, but are intended to allow readers to “track” labels across figures.

+

Where there’s a 1:1 mapping between broad_histology and cancer_group, the hex codes will be the same.

- -
benign <- c("benign tumor", "non-tumor")
+ +
cancer_group_df <- data.frame(
+  cancer_group = c("Choroid plexus papilloma",
+                   "Diffuse intrinsic pontine glioma",
+                   "Diffuse midline glioma",
+                   "High-grade glioma astrocytoma",
+                   "Atypical Teratoid Rhabdoid Tumor",
+                   "CNS Embryonal tumor",
+                   "Medulloblastoma",
+                   "Ependymoma",
+                   "Teratoma",
+                   "Ganglioglioma",
+                   "Low-grade glioma astrocytoma",
+                   "Meningioma",
+                   "Ewing sarcoma",
+                   "Dysembryoplastic neuroepithelial tumor",
+                   "Neurofibroma Plexiform",
+                   "Schwannoma",
+                   "Craniopharyngioma"),
+  cancer_group_hex = c("#4d2635",
+                       "#bf0099",
+                       "#ff40d9",
+                       "#ffccf5",
+                       "#4d0d85",
+                       "#b08ccf",
+                       "#a340ff",
+                       "#2200ff",
+                       "#058aff",
+                       "#8c8cff",
+                       "#000080",
+                       "#2db398",
+                       "#9fbf60",
+                       "#614e01",
+                       "#e6ac39",
+                       "#ab7200",
+                       "#b33000"),
+  stringsAsFactors = FALSE
+)
+# Value for "other" groups
+cancer_group_other_hex <- "#b5b5b5"
-

Add in the Other astrocytic tumor in with the LGAT group.

+

And again, we’ll create a legend with legend()

- -
lgat <- c("other astrocytic tumor", "low-grade astrocytic tumor")
+ +
plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", 
+     xlim = 0:1, ylim = 0:1)
+legend("topleft", 
+       legend = c(cancer_group_df$cancer_group, "Other"),
+       col = c(cancer_group_df$cancer_group_hex, cancer_group_other_hex),
+       pch = 15, pt.cex = 1.5, cex = 0.75, bty = "n")
- - -
-
-
-

Make new display_group

- - - -
histology_table <- working_metadata %>% 
-  dplyr::mutate(
-    # NAs are really Normals
-    display_group = tidyr::replace_na(broad_histology_lower, "normal"),
-    # Now do the group combining
-    display_group = forcats::fct_collapse(display_group,
-      "low-grade astrocytic tumor" = lgat,
-      "other tumor" = other_tumor,
-      "benign" = benign
-    ),
-    # Put this as a character for later handling
-    display_group = as.character(display_group)
-    )
+ +
mtext("Cancer Group", at = 0.0625, cex = 1)
- -
Warning: Unknown levels in `f`: other astrocytic tumor
- + +

+ -

Print out the number of display_group (including normal)!

+
+
+

Output

+

We can create a data frame that contains both palettes with a series of left joins, where we will then fill the NA values with a single (gray) hex code per column (#808080 for broad_histology, #b5b5b5 for cancer_group.)

- -
display_group_df <- histology_table %>% 
-  dplyr::count(display_group) %>% 
-  dplyr::arrange(n)
-
-knitr::kable(display_group_df)
+ +
palette_df <- histologies_df %>%
+  # Exclude normal samples
+  filter(sample_type == "Tumor") %>%
+  # Filter to unique broad histology--cancer group pairs
+  select(broad_histology, 
+         cancer_group) %>% 
+  distinct() %>%
+  # Add broad histology palette
+  left_join(broad_histology_df, by = "broad_histology") %>%
+  # Add cancer group palette
+  left_join(cancer_group_df, by = "cancer_group") %>%
+  # Fill all other values with gray colors
+  replace_na(list(broad_histology_hex = broad_histology_other_hex,
+                  cancer_group_hex = cancer_group_other_hex)) %>%
+  # The exception being - if cancer_group == NA, so should cancer_group_hex!
+  mutate(cancer_group_hex = if_else(is.na(cancer_group), 
+                                    NA_character_, 
+                                    cancer_group_hex)) %>%
+  # Sort by broad_histology for easy browsing
+  arrange(broad_histology)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
display_groupn
choroid plexus tumor9
tumor of pineal region10
chordoma12
histiocytic tumor14
other tumor21
germ cell tumor27
pre-cancerous lesion27
mesenchymal non-meningothelial tumor49
meningioma59
tumors of sellar region73
benign76
neuronal and mixed neuronal-glial tumor83
tumor of cranial and paraspinal nerves83
ependymal tumor174
embryonal tumor333
diffuse astrocytic and oligodendroglial tumor370
low-grade astrocytic tumor587
normal833
-

Make this notebook stop if there are more than 16 histology groups + Normal.

+

And now let’s take a look!

- -
if (nrow(display_group_df) > 18) {
-  stop("There are more than 18 categories in `display_group`. We may want to re-evaluate the high-level histology groupings")
-}
+ +
palette_df
+ +
+ +
+
-
-

Make display_order

-

Get ranks in order of big to small and make them into a new column in display_group_df. We will always want the “normal”, “benign”, “other_tumor” groups to come last so we will push then to the end of the factor order.

+
+

Add display names for convenience

+

When multiple values are using the same color, it can be helpful to have a separate value for the legend, e.g., for all #808080 broad histologies, we may want to display Other. We’ll add a couple columns for legend-making convenience.

- -
display_order_df <- display_group_df %>% 
-  dplyr::mutate(display_group = forcats::fct_reorder(display_group, n, .desc = TRUE) %>%
-                  forcats::fct_relevel("benign", "other tumor", "normal", after = Inf),
-                display_order = as.numeric(display_group)) # save the factor order for text table export
+ +
palette_df <- palette_df %>%
+  mutate(broad_histology_display = if_else(broad_histology_hex == broad_histology_other_hex,
+                                           "Other",
+                                           broad_histology),
+         cancer_group_display = if_else(cancer_group_hex == cancer_group_other_hex,
+                                        "Other",
+                                        cancer_group))
-
-

Make cancer_group_order

-

cancer_group is a shorter form of harmonized_diagnosis with the following edits: - Removed Other, Benign tumor and Dysplasia/Gliosis,Dysplasia/Gliosis-Glial-neuronal tumor NOS removed from cancer_group - Neurofibroma/Plexiform;Other updated to Neurofibroma/Plexiform - Non-germinomatous germ cell tumor;Teratoma updated to Teratoma - Anaplastic (malignant) meningioma, Meningothelial meningioma and Clear cell meningioma updated to Meningioma - Embryonal Tumor with Multilayered Rosettes updated to Embryonal tumor with multilayer rosettes

-

Get ranks in order of big to small and make them into a new dataframe cancer_group_order_df.

+
+

Add broad_histology_order

+

Previously, we had a concept known as display_order where we ordered categories based on their number of samples (from large to small). Now that we’ve dropped display_group, let’s apply this same concept to broad_histology.

- -
cancer_group_order_df <- histology_table %>% 
-  dplyr::count(cancer_group,name = "cancer_group_n") %>% 
-  dplyr::mutate(
-    cancer_group = forcats::fct_reorder(cancer_group, cancer_group_n, .desc = TRUE),
-                cancer_group_order = as.numeric(cancer_group)) # save the factor order for text table export
+ +
broad_histology_order_df <- histologies_df %>%  
+  # Exclude normal samples
+  filter(sample_type == "Tumor",
+         # Only count histologies that we'll have a hex code for
+         broad_histology %in% included_labels_df$broad_histology) %>%
+  # Filter to unique sample--broad_histology pairs
+  select(sample_id, 
+         broad_histology) %>%
+  distinct() %>%
+  # Count samples within a broad histology
+  count(broad_histology) %>%
+  # Add Other placeholder
+  bind_rows(data.frame(broad_histology = "Other",
+                       n = 0, 
+                       stringsAsFactors = FALSE)) %>%
+  # Reorder based on sample size except Benign tumor and Other should come last
+  # And then add numeric column with the order
+  mutate(broad_histology = forcats::fct_reorder(broad_histology,
+                                                n,
+                                                .desc = TRUE) %>%
+           forcats::fct_relevel("Benign tumor",
+                                "Other",
+                                after = Inf),
+         broad_histology_order = as.numeric(broad_histology)) %>%
+  # No longer require the sample size
+  select(-n)
+
+broad_histology_order_df
+ +
+ +
+ -

Add on the display_order column using inner_join

+

And now we’re ready to add this to the palette data frame.

- -
histology_table <- histology_table %>%
-  # Join on the display orders
-  dplyr::inner_join(display_order_df, by = "display_group") %>%
-  # Join on the cancer_group orders
-  dplyr::inner_join(cancer_group_order_df, by = "cancer_group") 
+ +
palette_df <- palette_df %>%
+  left_join(broad_histology_order_df, 
+            by = c("broad_histology_display" = "broad_histology"))
- -
Warning: Column `display_group` joining character vector and factor,
-coercing into character vector
- - -
Warning: Column `cancer_group` joining character vector and factor,
-coercing into character vector
- + +
Column `broad_histology_display`/`broad_histology` joining character vector and factor, coercing into character vector
+
-
-

Add hex codes for display_group and cancer_group

-

These hex codes were retrieved from http://phrogz.net/css/distinct-colors.html with the settings on default for 18 colors.

+
+

Add oncoprint_group and oncoprint_hex

+

For most plots that make use of the cancer_group palette, such as a box or violin plot, we will rely heavily on labels and therefore using the gray hex code for multiple groups will not be a problem.

+

We will have four panels of individual oncoprints, where many broad_histology values will get grouped together into the Other CNS panel which you can see here. We can move this information into our palette data frame.

- -
color_palette_display <- 
-  c("#ff0000", "#cc0000", "#995200", "#bfb300", "#fffbbf", 
-    "#2e7300", "#00e65c", "#00ffee", "#103d40", "#0085a6", 
-    "#003380", "#4073ff", "#737899", "#70008c", "#f2b6ee", 
-    "#ff40bf", "#8c0038", "#330d12"
+
+
# Taken from the current plot oncoprint script as of the writing of this
+# See permalink above
+other_cns_broad_histologies <- c(
+  "Ependymal tumor",
+  "Tumors of sellar region",
+  "Neuronal and mixed neuronal-glial tumor",
+  "Tumor of cranial and paraspinal nerves",
+  "Meningioma",
+  "Mesenchymal non-meningothelial tumor",
+  "Germ cell tumor",
+  "Choroid plexus tumor",
+  "Histiocytic tumor",
+  "Tumor of pineal region",
+  "Metastatic tumors",
+  "Other astrocytic tumor",
+  "Lymphoma",
+  "Melanocytic tumor",
+  "Other tumor"
 )
 
-color_palette_cancer_group <-
-  c("#ff0000", "#f20000", "#997373", "#403030", "#330700",
-    "#ff9180", "#591800", "#b2502d", "#cca799", "#ff6600",
-    "#ffb380", "#7f5940", "#cc6d00", "#331b00", "#ccb499",
-    "#ffaa00", "#996600", "#594316", "#ffd580", "#ffee00",
-    "#998f00", "#999673", "#303300", "#fbffbf", "#ccff00",
-    "#494d39", "#b5d96c", "#6a8040", "#66ff00", "#42a600",
-    "#bfffbf", "#003307", "#00661b", "#00ff88", "#86b39e",
-    "#00b377", "#006652", "#00ffee", "#00a7b3", "#bffbff",
-    "#567173", "#00ccff", "#003d4d", "#00aaff", "#267399",
-    "#0088ff", "#0042a6", "#001a40", "#bfd9ff", "#0044ff",
-    "#394973", "#000e66", "#bfbfff", "#9180ff", "#5800a6",
-    "#754d99", "#aa00ff", "#3a3040", "#aa86b3", "#530059",
-    "#ff00ee", "#a60085", "#330022", "#ff80d5", "#ff0088",
-    "#804062", "#a60042", "#590024", "#ffbfd9", "#ff0044",
-    "#990014", "#ff8091"
-)
+palette_df <- palette_df %>% + mutate(oncoprint_group = case_when( + broad_histology %in% other_cns_broad_histologies ~ "Other CNS", + broad_histology %in% c( + "Low-grade astrocytic tumor", + "Embryonal tumor", + "Diffuse astrocytic and oligodendroglial tumor" + ) ~ broad_histology, + TRUE ~ NA_character_ + ))
-

Declare how many colors we need.

+

For cancer groups that do not get their own hex code for display (i.e., due to small sample sizes), we’ll use a selection of grey colors as a palette and rely heavily on the ordering of the OncoPrint legend. Unfortunately there are over 20 “Other CNS” cancer groups that meet this criterion, so it is not feasible to have a color for each of them and they will not be included in the “Other CNS” OncoPrint.

- -
n_colors_display <- nrow(display_group_df)
-n_colors_cancer_group <- nrow(cancer_group_order_df)
- - - -

Make a named list color key where histologies are the names.

- - - -
# Set seed so the colors are consistent upon re-run
-set.seed(2021)
+
+
greys_df <- palette_df %>%
+  filter(cancer_group_display == "Other",
+         !is.na(oncoprint_group),
+         oncoprint_group != "Other CNS")
 
-# Sample from the 18 colors for display_group
-subset_colors_display <- sample(color_palette_display, n_colors_display)
-names(subset_colors_display) <- display_order_df$display_group
-
-# Sample from the 62 colors for cancer_group
-subset_colors_cancer_group <- sample(color_palette_cancer_group, n_colors_cancer_group)
-names(subset_colors_cancer_group) <- cancer_group_order_df$cancer_group
- - - -

Remove from subset_colors_cancer_group

- - - -
# We will assign a gray color for NA below
-subset_colors_cancer_group <- subset_colors_cancer_group[!is.na(names(subset_colors_cancer_group))]
- - - -

We want Other tumor and the Benign in display_group to both always be gray.

- - - -
subset_colors_display[names(subset_colors_display) == 'other tumor'] <- "#808080" 
-subset_colors_display[names(subset_colors_display) == 'benign'] <-  "#D3D3D3"
- - - -

Normal biospecimens should not get plotted in display_group, so we will put their hex code as black.

- - - -
subset_colors_display[names(subset_colors_display) == 'normal'] <- "#000000"
- - - -

Use pie function to preview what display_group these look like.

- - - -
pie(rep(1, n_colors_display), 
-    col = subset_colors_display, 
-    labels = names(subset_colors_display))
- - -

- - - -

Use pie function to preview what cancer_group these look like.

- - - -
pie(rep(1, n_colors_cancer_group), 
-    col = subset_colors_cancer_group, 
-    labels = names(subset_colors_cancer_group))
+# Sample the greys sequential palette from color brewer +set.seed(2021) +greys_df <- greys_df %>% + mutate(oncoprint_hex = sample(brewer.pal(nrow(greys_df), "Greys")))
- -

- -

Add the hex codes for display_group and cancer_group to the histology_table. Add gray color if cancer_group==NA

+

Add oncoprint_hex & oncoprint_include, the latter will be FALSE when the former is NA.

+

Add oncoprint_hex for all other cancer groups and oncoprint_include. The latter will be FALSE when the former is NA.

- -
histology_table <- histology_table %>%
-  # We don't need this anymore
-  dplyr::select(-broad_histology_lower) %>%
-  # Add the hex_codes
-  dplyr::mutate(hex_codes = dplyr::recode(display_group, !!!subset_colors_display),
-        cancer_group_hex_codes = dplyr::recode(cancer_group, !!!subset_colors_cancer_group),
-                # if cancer_group is NA for tumor sample add gray color
-        cancer_group_hex_codes = dplyr::if_else(is.na(cancer_group_hex_codes) & sample_type=="Tumor"
-                                                 ,"#808080",
-                                                 cancer_group_hex_codes)
-            ) %>% 
-  # Restore capitalization so its pretty for labeling
-  dplyr::mutate(display_group = stringr::str_to_sentence(display_group),
-                # Deal with CNS exception
-                display_group = stringr::str_replace(display_group, "cns", "CNS")
-                )
+ +
palette_df <- palette_df %>%
+  left_join(greys_df) %>%
+  # When there's an oncoprint group and a specific cancer group display color,
+  # use the cancer group display color as the oncoprint color
+  mutate(oncoprint_hex = if_else(
+    cancer_group_hex != cancer_group_other_hex & !is.na(oncoprint_group),
+    cancer_group_hex,
+    oncoprint_hex
+  ),
+  # Only when there's a specific oncoprint color -- even if that is a grey 
+  # selected only for the oncoprint -- will a cancer group be included in the
+  # oncoprint
+  oncoprint_include = if_else(
+    is.na(oncoprint_hex),
+    FALSE,
+    TRUE
+  ))
+ +
Joining, by = c("broad_histology", "cancer_group", "broad_histology_hex", "cancer_group_hex", "broad_histology_display", "cancer_group_display", "broad_histology_order", "oncoprint_group")
+ -
-

Save to TSV

+
+
+

Save to TSV

- -
readr::write_tsv(histology_table, file.path(output_dir, "histology_label_color_table.tsv"))
+ +
readr::write_tsv(palette_df, 
+                 file.path(output_dir, 
+                           "broad_histology_cancer_group_palette.tsv"))
-
-

Session Info

+
+

Session Info

- +
sessionInfo()
- +
R version 3.6.0 (2019-04-26)
 Platform: x86_64-pc-linux-gnu (64-bit)
 Running under: Debian GNU/Linux 9 (stretch)
@@ -3543,20 +3437,28 @@ 

Session Info

attached base packages: [1] stats graphics grDevices utils datasets methods base +other attached packages: + [1] RColorBrewer_1.1-2 forcats_0.4.0 stringr_1.4.0 + [4] dplyr_0.8.3 purrr_0.3.2 readr_1.3.1 + [7] tidyr_0.8.3 tibble_2.1.3 ggplot2_3.2.0 +[10] tidyverse_1.2.1 + loaded via a namespace (and not attached): - [1] Rcpp_1.0.1 knitr_1.23 magrittr_1.5 hms_0.4.2 - [5] tidyselect_0.2.5 R6_2.4.0 rlang_0.4.0 stringr_1.4.0 - [9] highr_0.8 dplyr_0.8.3 tools_3.6.0 xfun_0.8 -[13] ellipsis_0.2.0.1 htmltools_0.3.6 yaml_2.2.0 assertthat_0.2.1 -[17] digest_0.6.20 tibble_2.1.3 crayon_1.3.4 purrr_0.3.2 -[21] readr_1.3.1 tidyr_0.8.3 base64enc_0.1-3 glue_1.3.1 -[25] evaluate_0.14 rmarkdown_1.13 stringi_1.4.3 compiler_3.6.0 -[29] pillar_1.4.2 forcats_0.4.0 jsonlite_1.6 pkgconfig_2.0.2
+ [1] Rcpp_1.0.1 cellranger_1.1.0 pillar_1.4.2 compiler_3.6.0 + [5] tools_3.6.0 jsonlite_1.6 lubridate_1.7.4 nlme_3.1-140 + [9] gtable_0.3.0 lattice_0.20-38 pkgconfig_2.0.2 rlang_0.4.0 +[13] cli_1.1.0 rstudioapi_0.10 haven_2.1.1 xfun_0.8 +[17] withr_2.1.2 xml2_1.2.0 httr_1.4.0 knitr_1.23 +[21] generics_0.0.2 hms_0.4.2 grid_3.6.0 tidyselect_0.2.5 +[25] glue_1.3.1 R6_2.4.0 readxl_1.3.1 modelr_0.1.4 +[29] magrittr_1.5 ellipsis_0.2.0.1 backports_1.1.4 scales_1.0.0 +[33] rvest_0.3.4 assertthat_0.2.1 colorspace_1.4-1 stringi_1.4.3 +[37] lazyeval_0.2.2 munsell_0.5.0 broom_0.5.2 crayon_1.3.4
-
LS0tCnRpdGxlOiAiTWFwcGluZyBoaXN0b2xvZ3kgbGFiZWxzIGZvciBwbG90cyIKb3V0cHV0OiAgIAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKYXV0aG9yOiBDYW5kYWNlIFNhdm9uZW4gZm9yIEFMU0YgLSBDQ0RMCmRhdGU6IDIwMjEKLS0tCgojIFB1cnBvc2U6IAoKVGhlIGhpc3RvbG9neSBsYWJlbCB2YXJpYWJsZXMgaW5jbHVkZWQgaW4gYHBidGEtaGlzdG9sb2dpZXMudHN2YCBmcm9tIGRhdGEgcmVsZWFzZXMgYXJlIG5vdCBhbHdheXMgdXNlZnVsIGZvciB2aXN1YWxpemluZyB0aGUgZnVsbCBzZXQgb2YgYmlvc3BlY2ltZW5zIGR1ZSB0byB0aGUgbGFyZ2UgbnVtYmVyIG9mIGRpZmZlcmVudCB2YWx1ZXMuCkhhdmluZyB0b28gbWFueSBkaWZmZXJlbnQgcG9zc2libGUgdmFsdWVzIG1ha2VzIHRoZSBjb2xvcnMgaGFyZGVyIHRvIGRpc3Rpbmd1aXNoLgpJbiBhZGRpdGlvbiwgdGhlcmUgYXJlIHNvbWUgZ3JvdXBzIHRoYXQgYXJlIHJlcHJlc2VudGVkIGJ5IG9ubHkgYSB2ZXJ5IGZldyBzYW1wbGVzOyBnaXZpbmcgc3VjaCBncm91cHMgYSBkaXN0aW5jdCBjb2xvciBtYXkgYmUgY291bnRlcnByb2R1Y3RpdmUuCgpUaGUgZ29hbCBvZiB0aGlzIG5vdGVib29rIGlzIHRvIHVzZSB0aGUgY3VycmVudGx5IGV4aXN0aW5nIGBicm9hZF9oaXN0b2xvZ3lgIGdyb3VwcyBmcm9tIGBwYnRhLWhpc3RvbG9naWVzLnRzdmAsIHRvIGZvcm0gMTAtMTUgImhpZ2ggbGV2ZWwgaGlzdG9sb2d5IiBncm91cCBsYWJlbHMgdGhhdCBjYW4gdXNlZCBmb3IgcGxvdHRpbmcgcHVycG9zZXMuCgojIyBUaGUgb3V0cHV0IHRhYmxlCgpUaGUgb3V0cHV0IG9mIHRoaXMgbm90ZWJvb2sgaXMgYSBUU1YgZmlsZTogYHBhbGV0dGVzL2hpc3RvbG9neV9sYWJlbF9jb2xvcl90YWJsZS50c3ZgIHRoYXQgY29udGFpbnMgdGhlIGZvbGxvd2luZyBmaWVsZHM6CgoqKkNvcGllZCBmcm9tIGBwYnRhLWhpc3RvbG9naWVzLnRzdmAqKjogICAgCi0gYEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURgIChmcm9tIGBwYnRhLWhpc3RvbG9naWVzLnRzdmApICAKLSBBbGwgdGhlIG9yaWdpbmFsIGhpc3RvbG9neSBsYWJlbCB2YXJpYWJsZXMgKGBicm9hZF9oaXN0b2xvZ3lgLCBgc2hvcnRfaGlzdG9sb2d5YCwgZXRjLikgIAogIAoqKkNyZWF0ZWQgaW4gdGhpcyBub3RlYm9vayoqOiAgCi0gYGRpc3BsYXlfZ3JvdXBgIC0gdGhlIGhpZ2gtbGV2ZWwgaGlzdG9sb2d5IGxhYmVscyB0aGF0IHNob3VsZCBiZSB1c2VkIGZvciBwbG90dGluZyAgIAotIGBoZXhfY29kZXNgIHRoZSBkaXJlY3QgY29sb3JzIHRoYXQgY29ycmVzcG9uZCB0byBkaXNwbGF5X2dyb3VwcwotIGBjYW5jZXJfZ3JvdXBfaGV4X2NvZGVzYCB0aGUgZGlyZWN0IGNvbG9ycyB0aGF0IGNvcnJlc3BvbmQgdG8gY2FuY2VyX2dyb3VwcwoKV2l0aCB0aGlzIGluZm8sIGBoaXN0b2xvZ3lfbGFiZWxfY29sb3JfdGFibGUudHN2YCBjYW4gYmUgdXNlZCBieSBhbGwgcGxvdHMgYW5kIGZpZ3VyZXMgdGhhdCBzdW1tYXJpemUgaGlnaCBsZXZlbCBkYXRhICB3aGlsZSBkaXNwbGF5aW5nIGhpc3RvbG9neSBpbmZvcm1hdGlvbi4gCgojIEhvdyBgZGlzcGxheV9ncm91cGAgaXMgbWFkZToKCkhlcmUncyBob3cgYGJyb2FkLWhpc3RvbG9neWAgZ3JvdXBzIGFyZSBbY29tYmluZWQgaW50byB0aGUgaGlnaGVyLWxldmVsIGdyb3VwaW5ncyBvZiBgZGlzcGxheV9ncm91cGBdKCNkZWNsYXJlLW5ldy1lcXVpdmFsZW50LWdyb3VwcykuCgoxKSAiTHltcGhvbWEiLCAiTWVsYW5vY3l0aWMgdHVtb3IiLCAiT3RoZXIgdHVtb3IiLCAiTWV0YXN0YXRpYyB0dW1vcnMiLCAiTm9uLUNOUyB0dW1vciIgYXJlIGNvbWJpbmVkIGludG8gYSBgT3RoZXIgdHVtb3JgIGluIGBkaXNwbGF5X2dyb3VwYC4gCgoyKSBgQmVuaWduIHR1bW9yYCBhbmQgYE5vbi10dW1vcmAgYmlvc3BlY2ltZW5zIGFyZSBjb21iaW5lZCBpbnRvIGEgYEJlbmlnbmAgZ3JvdXAuIAoKMykgYE90aGVyIGFzdHJvY3l0aWMgdHVtb3JgIGJpb3NwZWNpbWVucyBhcmUgY29tYmluZWQgaW50byB0aGUgZXhpc3RpbmcgYExvdy1ncmFkZSBhc3Ryb2N5dGljIHR1bW9yYC4gVGhlc2UgYmlvc3BlY2ltZW5zICBpbiBgb3RoZXIgYXN0cm9jeXRpYyB0dW1vcnNgIHdlcmUgbG93LWdyYWRlIFNFR0EgdHVtb3JzLiAKCjQpIEFueXRoaW5nIG5vdCBpbiB0aGUgYWJvdmUgY2F0ZWdvcmllcyBnZXRzIGl0cyBgYnJvYWRfaGlzdG9sb2d5YCBsYWJlbCBjYXJyaWVkIG92ZXIuIAoKIyBVc2FnZQoKVGhpcyBub3RlYm9vayBjYW4gYmUgcnVuIHZpYSB0aGUgY29tbWFuZCBsaW5lIGZyb20gdGhlIHRvcCBkaXJlY3Rvcnkgb2YgdGhlIApyZXBvc2l0b3J5IGFzIGZvbGxvd3M6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2ZpZ3VyZXMvbWFwcGluZy1oaXN0b2xvZ3ktbGFiZWxzLlJtZCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhbiA9IFRSVUUpIgpgYGAKCiMjIFNldCBVcAoKYGBge3J9CiMgTWFncml0dHIgcGlwZQpgJT4lYCA8LSBkcGx5cjo6YCU+JWAKYGBgCgojIyMgRGlyZWN0b3JpZXMgYW5kIEZpbGVzCgpgYGB7cn0KIyBQYXRoIHRvIGlucHV0IGRpcmVjdG9yeQppbnB1dF9kaXIgPC0gZmlsZS5wYXRoKCIuLiIsICJkYXRhIikKb3V0cHV0X2RpciA8LSAicGFsZXR0ZXMiCmBgYAoKIyBSZWFkIGluIG1ldGFkYXRhIAoKV2hpY2ggdmFyaWFibGVzIGFyZSB3ZSBrZWVwaW5nIGZvciB0aGlzIHRhYmxlPyAKCmBgYHtyfQpoaXN0b2xvZ3lfdmFyaWFibGVzIDwtIAogIGMoImludGVncmF0ZWRfZGlhZ25vc2lzIiwgCiAgICAiTm90ZXMiLCAKICAgICJoYXJtb25pemVkX2RpYWdub3NpcyIsCiAgICAiYnJvYWRfaGlzdG9sb2d5IiwgCiAgICAic2hvcnRfaGlzdG9sb2d5IiwKICAgICJjYW5jZXJfZ3JvdXAiKQpgYGAKCkxldCdzIHJlYWQgaW4gdGhlIGN1cnJlbnQgcmVsZWFzZSdzIGBwYnRhLWhpc3RvbG9naWVzLnRzdmAgZmlsZS4gCgpgYGB7cn0KbWV0YWRhdGEgPC0KICByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKGlucHV0X2RpciwgInBidGEtaGlzdG9sb2dpZXMudHN2IiksIGd1ZXNzX21heCA9IDEwMDAwKQpgYGAKCk5vdyB3ZSdsbCBzZWxlY3QgaGlzdG9sb2d5IHZhcmlhYmxlcyB3ZSBtZW50aW9uZWQgYWJvdmUgYW5kIHNvIGNhcGl0YWxpemF0aW9uIGRpZmZlcmVuY2VzIGRvbid0IGdldCBpbiB0aGUgd2F5IHdpdGggdGhpcyBwcm9jZXNzLCB3ZSB3aWxsIGNoYW5nZSBldmVyeXRoaW5nIHRvIGxvd2VyIGNhc2UgZm9yIG5vdy4gCgpgYGB7cn0Kd29ya2luZ19tZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgCiAgZHBseXI6OnNlbGVjdChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBzYW1wbGVfdHlwZSwgaGlzdG9sb2d5X3ZhcmlhYmxlcykgJT4lIAogIGRwbHlyOjptdXRhdGUoYnJvYWRfaGlzdG9sb2d5X2xvd2VyID0gdG9sb3dlcihicm9hZF9oaXN0b2xvZ3kpKQpgYGAKCiMgVGFrZSBhIGxvb2sgYXQgaG93IG1hbnkgYmlvc3BlY2ltZW5zIHBlciBgYnJvYWRfaGlzdG9sb2d5YCBncm91cAoKTGV0J3Mgc3VtbWFyaXplIGBicm9hZF9oaXN0b2xvZ3lgLiAKQmVjYXVzZSB0aGUgYE5vcm1hbGAgc2FtcGxlcyBkb24ndCBoYXZlIGhpc3RvbG9naWVzLCB3ZSdsbCBsb29rIGF0IGp1c3QgdGhlIGBUdW1vcmAgc2FtcGxlcyBhdCBmb3IgdGhpcyBzdW1tYXJ5LiAKCmBgYHtyfQpicm9hZF9zdW1tYXJ5IDwtIHdvcmtpbmdfbWV0YWRhdGEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoc2FtcGxlX3R5cGUgPT0gIlR1bW9yIikgJT4lCiAgZHBseXI6OmNvdW50KGJyb2FkX2hpc3RvbG9neV9sb3dlcikgJT4lIAogIGRwbHlyOjphcnJhbmdlKG4pIApgYGAKCkxldCdzIHByaW50IG91dCB0aGUgc3VtbWFyeS4gCgpgYGB7cn0KYnJvYWRfc3VtbWFyeSAlPiUgCiAga25pdHI6OmthYmxlKCkKYGBgCgpUaGVyZSdzIGhhbmRmdWwgb2YgdmVyeSBzbWFsbCBncm91cHMgKG1hbnkgYXJlIG4gPSAyKS4gCgojIyBEZWNsYXJlIG5ldyBlcXVpdmFsZW50IGdyb3VwcwoKVGhlc2UgZ3JvdXBzIHdlJ2xsIGNvbWJpbmUgaW50byBhIG5vbi1DTlMvb3RoZXIgdHVtb3IgZ3JvdXAuCgpgYGB7cn0Kb3RoZXJfdHVtb3IgPC0gYygibHltcGhvbWEiLCAibWVsYW5vY3l0aWMgdHVtb3IiLCAib3RoZXIgdHVtb3IiLCAibWV0YXN0YXRpYyB0dW1vcnMiLCAibm9uLWNucyB0dW1vciIpCmBgYAoKVGhlc2UgZ3JvdXBzIHdlJ2xsIGNvbWJpbmUgYXMgYSBiZW5pZ24uCgpgYGB7cn0KYmVuaWduIDwtIGMoImJlbmlnbiB0dW1vciIsICJub24tdHVtb3IiKQpgYGAKCkFkZCBpbiB0aGUgYE90aGVyIGFzdHJvY3l0aWMgdHVtb3JgIGluIHdpdGggdGhlIExHQVQgZ3JvdXAuIAoKYGBge3J9CmxnYXQgPC0gYygib3RoZXIgYXN0cm9jeXRpYyB0dW1vciIsICJsb3ctZ3JhZGUgYXN0cm9jeXRpYyB0dW1vciIpCmBgYAoKIyBNYWtlIG5ldyBgZGlzcGxheV9ncm91cGAKCmBgYHtyfQpoaXN0b2xvZ3lfdGFibGUgPC0gd29ya2luZ19tZXRhZGF0YSAlPiUgCiAgZHBseXI6Om11dGF0ZSgKICAgICMgTkFzIGFyZSByZWFsbHkgTm9ybWFscwogICAgZGlzcGxheV9ncm91cCA9IHRpZHlyOjpyZXBsYWNlX25hKGJyb2FkX2hpc3RvbG9neV9sb3dlciwgIm5vcm1hbCIpLAogICAgIyBOb3cgZG8gdGhlIGdyb3VwIGNvbWJpbmluZwogICAgZGlzcGxheV9ncm91cCA9IGZvcmNhdHM6OmZjdF9jb2xsYXBzZShkaXNwbGF5X2dyb3VwLAogICAgICAibG93LWdyYWRlIGFzdHJvY3l0aWMgdHVtb3IiID0gbGdhdCwKICAgICAgIm90aGVyIHR1bW9yIiA9IG90aGVyX3R1bW9yLAogICAgICAiYmVuaWduIiA9IGJlbmlnbgogICAgKSwKICAgICMgUHV0IHRoaXMgYXMgYSBjaGFyYWN0ZXIgZm9yIGxhdGVyIGhhbmRsaW5nCiAgICBkaXNwbGF5X2dyb3VwID0gYXMuY2hhcmFjdGVyKGRpc3BsYXlfZ3JvdXApCiAgICApCmBgYAoKUHJpbnQgb3V0IHRoZSBudW1iZXIgb2YgYGRpc3BsYXlfZ3JvdXBgIChpbmNsdWRpbmcgYG5vcm1hbGApIQoKYGBge3J9CmRpc3BsYXlfZ3JvdXBfZGYgPC0gaGlzdG9sb2d5X3RhYmxlICU+JSAKICBkcGx5cjo6Y291bnQoZGlzcGxheV9ncm91cCkgJT4lIAogIGRwbHlyOjphcnJhbmdlKG4pCgprbml0cjo6a2FibGUoZGlzcGxheV9ncm91cF9kZikKYGBgCgpNYWtlIHRoaXMgbm90ZWJvb2sgc3RvcCBpZiB0aGVyZSBhcmUgbW9yZSB0aGFuIDE2IGhpc3RvbG9neSBncm91cHMgKyBgTm9ybWFsYC4gCgpgYGB7cn0KaWYgKG5yb3coZGlzcGxheV9ncm91cF9kZikgPiAxOCkgewogIHN0b3AoIlRoZXJlIGFyZSBtb3JlIHRoYW4gMTggY2F0ZWdvcmllcyBpbiBgZGlzcGxheV9ncm91cGAuIFdlIG1heSB3YW50IHRvIHJlLWV2YWx1YXRlIHRoZSBoaWdoLWxldmVsIGhpc3RvbG9neSBncm91cGluZ3MiKQp9CmBgYAoKIyBNYWtlIGBkaXNwbGF5X29yZGVyYAoKR2V0IHJhbmtzIGluIG9yZGVyIG9mIGJpZyB0byBzbWFsbCBhbmQgbWFrZSB0aGVtIGludG8gYSBuZXcgY29sdW1uIGluIGBkaXNwbGF5X2dyb3VwX2RmYC4gCldlIHdpbGwgYWx3YXlzIHdhbnQgdGhlICJub3JtYWwiLCAiYmVuaWduIiwgIm90aGVyX3R1bW9yIiBncm91cHMgdG8gY29tZSBsYXN0IHNvIHdlIHdpbGwgcHVzaCB0aGVuIHRvIHRoZSBlbmQgb2YgdGhlIGZhY3RvciBvcmRlci4gCgpgYGB7cn0KZGlzcGxheV9vcmRlcl9kZiA8LSBkaXNwbGF5X2dyb3VwX2RmICU+JSAKICBkcGx5cjo6bXV0YXRlKGRpc3BsYXlfZ3JvdXAgPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihkaXNwbGF5X2dyb3VwLCBuLCAuZGVzYyA9IFRSVUUpICU+JQogICAgICAgICAgICAgICAgICBmb3JjYXRzOjpmY3RfcmVsZXZlbCgiYmVuaWduIiwgIm90aGVyIHR1bW9yIiwgIm5vcm1hbCIsIGFmdGVyID0gSW5mKSwKICAgICAgICAgICAgICAgIGRpc3BsYXlfb3JkZXIgPSBhcy5udW1lcmljKGRpc3BsYXlfZ3JvdXApKSAjIHNhdmUgdGhlIGZhY3RvciBvcmRlciBmb3IgdGV4dCB0YWJsZSBleHBvcnQKYGBgCgojIE1ha2UgYGNhbmNlcl9ncm91cF9vcmRlcmAKCmBjYW5jZXJfZ3JvdXBgIGlzIGEgc2hvcnRlciBmb3JtIG9mIGBoYXJtb25pemVkX2RpYWdub3Npc2Agd2l0aCB0aGUgZm9sbG93aW5nIGVkaXRzOgotIFJlbW92ZWQgT3RoZXIsIEJlbmlnbiB0dW1vciBhbmQgRHlzcGxhc2lhL0dsaW9zaXMsRHlzcGxhc2lhL0dsaW9zaXMtR2xpYWwtbmV1cm9uYWwgdHVtb3IgTk9TIHJlbW92ZWQgZnJvbSBgY2FuY2VyX2dyb3VwYAotIE5ldXJvZmlicm9tYS9QbGV4aWZvcm07T3RoZXIgdXBkYXRlZCB0byBOZXVyb2ZpYnJvbWEvUGxleGlmb3JtCi0gTm9uLWdlcm1pbm9tYXRvdXMgZ2VybSBjZWxsIHR1bW9yO1RlcmF0b21hIHVwZGF0ZWQgdG8gVGVyYXRvbWEKLSBBbmFwbGFzdGljIChtYWxpZ25hbnQpIG1lbmluZ2lvbWEsIE1lbmluZ290aGVsaWFsIG1lbmluZ2lvbWEgYW5kIENsZWFyIGNlbGwgbWVuaW5naW9tYSB1cGRhdGVkIHRvIE1lbmluZ2lvbWEKLSBFbWJyeW9uYWwgVHVtb3Igd2l0aCBNdWx0aWxheWVyZWQgUm9zZXR0ZXMgdXBkYXRlZCB0byBFbWJyeW9uYWwgdHVtb3Igd2l0aCBtdWx0aWxheWVyIHJvc2V0dGVzCgpHZXQgcmFua3MgaW4gb3JkZXIgb2YgYmlnIHRvIHNtYWxsIGFuZCBtYWtlIHRoZW0gaW50byBhIG5ldyBkYXRhZnJhbWUgYGNhbmNlcl9ncm91cF9vcmRlcl9kZmAuIAogICAKYGBge3J9CmNhbmNlcl9ncm91cF9vcmRlcl9kZiA8LSBoaXN0b2xvZ3lfdGFibGUgJT4lIAogIGRwbHlyOjpjb3VudChjYW5jZXJfZ3JvdXAsbmFtZSA9ICJjYW5jZXJfZ3JvdXBfbiIpICU+JSAKICBkcGx5cjo6bXV0YXRlKAogICAgY2FuY2VyX2dyb3VwID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIoY2FuY2VyX2dyb3VwLCBjYW5jZXJfZ3JvdXBfbiwgLmRlc2MgPSBUUlVFKSwKICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9vcmRlciA9IGFzLm51bWVyaWMoY2FuY2VyX2dyb3VwKSkgIyBzYXZlIHRoZSBmYWN0b3Igb3JkZXIgZm9yIHRleHQgdGFibGUgZXhwb3J0CmBgYAoKCkFkZCBvbiB0aGUgYGRpc3BsYXlfb3JkZXJgIGNvbHVtbiB1c2luZyBgaW5uZXJfam9pbmAgCgpgYGB7cn0KaGlzdG9sb2d5X3RhYmxlIDwtIGhpc3RvbG9neV90YWJsZSAlPiUKICAjIEpvaW4gb24gdGhlIGRpc3BsYXkgb3JkZXJzCiAgZHBseXI6OmlubmVyX2pvaW4oZGlzcGxheV9vcmRlcl9kZiwgYnkgPSAiZGlzcGxheV9ncm91cCIpICU+JQogICMgSm9pbiBvbiB0aGUgY2FuY2VyX2dyb3VwIG9yZGVycwogIGRwbHlyOjppbm5lcl9qb2luKGNhbmNlcl9ncm91cF9vcmRlcl9kZiwgYnkgPSAiY2FuY2VyX2dyb3VwIikgCmBgYAoKIyBBZGQgaGV4IGNvZGVzIGZvciBkaXNwbGF5X2dyb3VwIGFuZCBjYW5jZXJfZ3JvdXAKClRoZXNlIGhleCBjb2RlcyB3ZXJlIHJldHJpZXZlZCBmcm9tIGh0dHA6Ly9waHJvZ3oubmV0L2Nzcy9kaXN0aW5jdC1jb2xvcnMuaHRtbCB3aXRoIHRoZSBzZXR0aW5ncyBvbiBkZWZhdWx0IGZvciAxOCBjb2xvcnMuCgpgYGB7cn0KY29sb3JfcGFsZXR0ZV9kaXNwbGF5IDwtIAogIGMoIiNmZjAwMDAiLCAiI2NjMDAwMCIsICIjOTk1MjAwIiwgIiNiZmIzMDAiLCAiI2ZmZmJiZiIsIAogICAgIiMyZTczMDAiLCAiIzAwZTY1YyIsICIjMDBmZmVlIiwgIiMxMDNkNDAiLCAiIzAwODVhNiIsIAogICAgIiMwMDMzODAiLCAiIzQwNzNmZiIsICIjNzM3ODk5IiwgIiM3MDAwOGMiLCAiI2YyYjZlZSIsIAogICAgIiNmZjQwYmYiLCAiIzhjMDAzOCIsICIjMzMwZDEyIgopCgpjb2xvcl9wYWxldHRlX2NhbmNlcl9ncm91cCA8LQogIGMoIiNmZjAwMDAiLCAiI2YyMDAwMCIsICIjOTk3MzczIiwgIiM0MDMwMzAiLCAiIzMzMDcwMCIsCiAgICAiI2ZmOTE4MCIsICIjNTkxODAwIiwgIiNiMjUwMmQiLCAiI2NjYTc5OSIsICIjZmY2NjAwIiwKICAgICIjZmZiMzgwIiwgIiM3ZjU5NDAiLCAiI2NjNmQwMCIsICIjMzMxYjAwIiwgIiNjY2I0OTkiLAogICAgIiNmZmFhMDAiLCAiIzk5NjYwMCIsICIjNTk0MzE2IiwgIiNmZmQ1ODAiLCAiI2ZmZWUwMCIsCiAgICAiIzk5OGYwMCIsICIjOTk5NjczIiwgIiMzMDMzMDAiLCAiI2ZiZmZiZiIsICIjY2NmZjAwIiwKICAgICIjNDk0ZDM5IiwgIiNiNWQ5NmMiLCAiIzZhODA0MCIsICIjNjZmZjAwIiwgIiM0MmE2MDAiLAogICAgIiNiZmZmYmYiLCAiIzAwMzMwNyIsICIjMDA2NjFiIiwgIiMwMGZmODgiLCAiIzg2YjM5ZSIsCiAgICAiIzAwYjM3NyIsICIjMDA2NjUyIiwgIiMwMGZmZWUiLCAiIzAwYTdiMyIsICIjYmZmYmZmIiwKICAgICIjNTY3MTczIiwgIiMwMGNjZmYiLCAiIzAwM2Q0ZCIsICIjMDBhYWZmIiwgIiMyNjczOTkiLAogICAgIiMwMDg4ZmYiLCAiIzAwNDJhNiIsICIjMDAxYTQwIiwgIiNiZmQ5ZmYiLCAiIzAwNDRmZiIsCiAgICAiIzM5NDk3MyIsICIjMDAwZTY2IiwgIiNiZmJmZmYiLCAiIzkxODBmZiIsICIjNTgwMGE2IiwKICAgICIjNzU0ZDk5IiwgIiNhYTAwZmYiLCAiIzNhMzA0MCIsICIjYWE4NmIzIiwgIiM1MzAwNTkiLAogICAgIiNmZjAwZWUiLCAiI2E2MDA4NSIsICIjMzMwMDIyIiwgIiNmZjgwZDUiLCAiI2ZmMDA4OCIsCiAgICAiIzgwNDA2MiIsICIjYTYwMDQyIiwgIiM1OTAwMjQiLCAiI2ZmYmZkOSIsICIjZmYwMDQ0IiwKICAgICIjOTkwMDE0IiwgIiNmZjgwOTEiCikKCmBgYAoKRGVjbGFyZSBob3cgbWFueSBjb2xvcnMgd2UgbmVlZC4gCgpgYGB7cn0Kbl9jb2xvcnNfZGlzcGxheSA8LSBucm93KGRpc3BsYXlfZ3JvdXBfZGYpCm5fY29sb3JzX2NhbmNlcl9ncm91cCA8LSBucm93KGNhbmNlcl9ncm91cF9vcmRlcl9kZikKYGBgCgpNYWtlIGEgbmFtZWQgbGlzdCBjb2xvciBrZXkgd2hlcmUgaGlzdG9sb2dpZXMgYXJlIHRoZSBuYW1lcy4gCgpgYGB7cn0KIyBTZXQgc2VlZCBzbyB0aGUgY29sb3JzIGFyZSBjb25zaXN0ZW50IHVwb24gcmUtcnVuCnNldC5zZWVkKDIwMjEpCgojIFNhbXBsZSBmcm9tIHRoZSAxOCBjb2xvcnMgZm9yIGRpc3BsYXlfZ3JvdXAKc3Vic2V0X2NvbG9yc19kaXNwbGF5IDwtIHNhbXBsZShjb2xvcl9wYWxldHRlX2Rpc3BsYXksIG5fY29sb3JzX2Rpc3BsYXkpCm5hbWVzKHN1YnNldF9jb2xvcnNfZGlzcGxheSkgPC0gZGlzcGxheV9vcmRlcl9kZiRkaXNwbGF5X2dyb3VwCgojIFNhbXBsZSBmcm9tIHRoZSA2MiBjb2xvcnMgZm9yIGNhbmNlcl9ncm91cApzdWJzZXRfY29sb3JzX2NhbmNlcl9ncm91cCA8LSBzYW1wbGUoY29sb3JfcGFsZXR0ZV9jYW5jZXJfZ3JvdXAsIG5fY29sb3JzX2NhbmNlcl9ncm91cCkKbmFtZXMoc3Vic2V0X2NvbG9yc19jYW5jZXJfZ3JvdXApIDwtIGNhbmNlcl9ncm91cF9vcmRlcl9kZiRjYW5jZXJfZ3JvdXAKYGBgCgpSZW1vdmUgPE5BPiBmcm9tIHN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwIApgYGB7cn0KIyBXZSB3aWxsIGFzc2lnbiBhIGdyYXkgY29sb3IgZm9yIE5BIGJlbG93CnN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwIDwtIHN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwWyFpcy5uYShuYW1lcyhzdWJzZXRfY29sb3JzX2NhbmNlcl9ncm91cCkpXQpgYGAKCldlIHdhbnQgYE90aGVyIHR1bW9yYCBhbmQgdGhlIGBCZW5pZ25gIGluIGRpc3BsYXlfZ3JvdXAgdG8gYm90aCBhbHdheXMgYmUgZ3JheS4gCgpgYGB7cn0Kc3Vic2V0X2NvbG9yc19kaXNwbGF5W25hbWVzKHN1YnNldF9jb2xvcnNfZGlzcGxheSkgPT0gJ290aGVyIHR1bW9yJ10gPC0gIiM4MDgwODAiIApzdWJzZXRfY29sb3JzX2Rpc3BsYXlbbmFtZXMoc3Vic2V0X2NvbG9yc19kaXNwbGF5KSA9PSAnYmVuaWduJ10gPC0gICIjRDNEM0QzIgpgYGAKCk5vcm1hbCBiaW9zcGVjaW1lbnMgc2hvdWxkIG5vdCBnZXQgcGxvdHRlZCBpbiBkaXNwbGF5X2dyb3VwLCBzbyB3ZSB3aWxsIHB1dCB0aGVpciBoZXggY29kZSBhcyBibGFjay4gCgpgYGB7cn0Kc3Vic2V0X2NvbG9yc19kaXNwbGF5W25hbWVzKHN1YnNldF9jb2xvcnNfZGlzcGxheSkgPT0gJ25vcm1hbCddIDwtICIjMDAwMDAwIgpgYGAKClVzZSBgcGllYCBmdW5jdGlvbiB0byBwcmV2aWV3IHdoYXQgZGlzcGxheV9ncm91cCB0aGVzZSBsb29rIGxpa2UuCgpgYGB7cn0KcGllKHJlcCgxLCBuX2NvbG9yc19kaXNwbGF5KSwgCiAgICBjb2wgPSBzdWJzZXRfY29sb3JzX2Rpc3BsYXksIAogICAgbGFiZWxzID0gbmFtZXMoc3Vic2V0X2NvbG9yc19kaXNwbGF5KSkKYGBgCgpVc2UgYHBpZWAgZnVuY3Rpb24gdG8gcHJldmlldyB3aGF0IGNhbmNlcl9ncm91cCB0aGVzZSBsb29rIGxpa2UuCgpgYGB7cn0KcGllKHJlcCgxLCBuX2NvbG9yc19jYW5jZXJfZ3JvdXApLCAKICAgIGNvbCA9IHN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwLCAKICAgIGxhYmVscyA9IG5hbWVzKHN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwKSkKYGBgCgoKQWRkIHRoZSBoZXggY29kZXMgZm9yIGRpc3BsYXlfZ3JvdXAgYW5kIGNhbmNlcl9ncm91cCB0byB0aGUgYGhpc3RvbG9neV90YWJsZWAuIApBZGQgZ3JheSBjb2xvciBpZiBjYW5jZXJfZ3JvdXA9PU5BCmBgYHtyfQpoaXN0b2xvZ3lfdGFibGUgPC0gaGlzdG9sb2d5X3RhYmxlICU+JQogICMgV2UgZG9uJ3QgbmVlZCB0aGlzIGFueW1vcmUKICBkcGx5cjo6c2VsZWN0KC1icm9hZF9oaXN0b2xvZ3lfbG93ZXIpICU+JQogICMgQWRkIHRoZSBoZXhfY29kZXMKICBkcGx5cjo6bXV0YXRlKGhleF9jb2RlcyA9IGRwbHlyOjpyZWNvZGUoZGlzcGxheV9ncm91cCwgISEhc3Vic2V0X2NvbG9yc19kaXNwbGF5KSwKCQljYW5jZXJfZ3JvdXBfaGV4X2NvZGVzID0gZHBseXI6OnJlY29kZShjYW5jZXJfZ3JvdXAsICEhIXN1YnNldF9jb2xvcnNfY2FuY2VyX2dyb3VwKSwKICAgICAgICAgICAgICAgICMgaWYgY2FuY2VyX2dyb3VwIGlzIE5BIGZvciB0dW1vciBzYW1wbGUgYWRkIGdyYXkgY29sb3IKCQljYW5jZXJfZ3JvdXBfaGV4X2NvZGVzID0gZHBseXI6OmlmX2Vsc2UoaXMubmEoY2FuY2VyX2dyb3VwX2hleF9jb2RlcykgJiBzYW1wbGVfdHlwZT09IlR1bW9yIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCIjODA4MDgwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9oZXhfY29kZXMpCgkgICAgICAgCSkgJT4lIAogICMgUmVzdG9yZSBjYXBpdGFsaXphdGlvbiBzbyBpdHMgcHJldHR5IGZvciBsYWJlbGluZwogIGRwbHlyOjptdXRhdGUoZGlzcGxheV9ncm91cCA9IHN0cmluZ3I6OnN0cl90b19zZW50ZW5jZShkaXNwbGF5X2dyb3VwKSwKICAgICAgICAgICAgICAgICMgRGVhbCB3aXRoIENOUyBleGNlcHRpb24KICAgICAgICAgICAgICAgIGRpc3BsYXlfZ3JvdXAgPSBzdHJpbmdyOjpzdHJfcmVwbGFjZShkaXNwbGF5X2dyb3VwLCAiY25zIiwgIkNOUyIpCiAgICAgICAgICAgICAgICApCmBgYAoKIyMgU2F2ZSB0byBUU1YgCgpgYGB7cn0KcmVhZHI6OndyaXRlX3RzdihoaXN0b2xvZ3lfdGFibGUsIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiaGlzdG9sb2d5X2xhYmVsX2NvbG9yX3RhYmxlLnRzdiIpKQpgYGAKCiMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK
+
LS0tCnRpdGxlOiAiQ3JlYXRlIGEgbWluaW1hbCBwYWxldHRlIGZvciBkaXNwbGF5aW5nIG11bHRpcGxlIGRpc2Vhc2UgbGFiZWxzIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IENhbmRhY2UgU2F2b25lbiwgS3J1dGlrYSBHYW9ua2FyLCBhbmQgSmFjbHluIFRhcm9uaQpkYXRlOiAyMDIxCi0tLQoKIyMgUHVycG9zZQoKVGhlcmUgYXJlIG11bHRpcGxlICJkaXNlYXNlIGxhYmVscyIgaW4gdGhlIGBwYnRhLWhpc3RvbG9naWVzLnRzdmAgZmlsZSwgaW5jbHVkaW5nIChmcm9tIG1vc3QgYnJvYWQgdG8gbW9zdCBuYXJyb3cpIGBicm9hZF9oaXN0b2xvZ3lgLCBgY2FuY2VyX2dyb3VwYCwgYW5kIGBoYXJtb25pemVkX2RpYWdub3Npc2AuCkZvciBjb250ZXh0LCBpdCBpcyBoZWxwZnVsIHRvIG5vdGUgdGhhdCBhbiBpbmRpdmlkdWFsIGBjYW5jZXJfZ3JvdXBgIHdpbGwgYmUgbmVzdGVkIHVuZGVyIGEgc2luZ2xlIGBicm9hZF9oaXN0b2xvZ3lgIGFuZCB0aGF0IGBjYW5jZXJfZ3JvdXBgIGlzIGEgc2hvcnRlciBmb3JtIG9mIGBoYXJtb25pemVkX2RpYWdub3Npc2Agd2l0aCB0aGUgZm9sbG93aW5nIGVkaXRzOgoKLSBPdGhlciwgQmVuaWduIHR1bW9yIGFuZCBEeXNwbGFzaWEvR2xpb3NpcywgRHlzcGxhc2lhL0dsaW9zaXMtR2xpYWwtbmV1cm9uYWwgdHVtb3IgTk9TIHJlbW92ZWQgZnJvbSBgY2FuY2VyX2dyb3VwYAotIE5ldXJvZmlicm9tYS9QbGV4aWZvcm07T3RoZXIgdXBkYXRlZCB0byBOZXVyb2ZpYnJvbWEvUGxleGlmb3JtCi0gTm9uLWdlcm1pbm9tYXRvdXMgZ2VybSBjZWxsIHR1bW9yO1RlcmF0b21hIHVwZGF0ZWQgdG8gVGVyYXRvbWEKLSBBbmFwbGFzdGljIChtYWxpZ25hbnQpIG1lbmluZ2lvbWEsIE1lbmluZ290aGVsaWFsIG1lbmluZ2lvbWEgYW5kIENsZWFyIGNlbGwgbWVuaW5naW9tYSB1cGRhdGVkIHRvIE1lbmluZ2lvbWEKLSBFbWJyeW9uYWwgVHVtb3Igd2l0aCBNdWx0aWxheWVyZWQgUm9zZXR0ZXMgdXBkYXRlZCB0byBFbWJyeW9uYWwgdHVtb3Igd2l0aCBtdWx0aWxheWVyIHJvc2V0dGVzCgpJdCBpcyBvZnRlbiB1c2VmdWwgdG8gdXNlIGNvbG9yIHRvIGluZGljYXRlIGRpc2Vhc2UgbGFiZWwgaW4gYSBwbG90IHdoZXJlIG11bHRpcGxlIGdyb3VwcyBhcmUgdmlzdWFsaXplZCB3aGVuIHdlIGNhbiBub3QgcmVseSBwYXJ0aWN1bGFybHkgaGVhdmlseSBvbiBsYWJlbHMgKGUuZy4sIHNjYXR0ZXIgcGxvdHMpLgpVbmZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgdG9vIG1hbnkgcG90ZW50aWFsIGxhYmVscyBmb3IgdXMgdG8gZ2VuZXJhdGUgYW4gZWZmZWN0aXZlIGNvbG9yIHBhbGV0dGUgKGUuZy4sIG9mIHN1ZmZpY2llbnRseSBkaXN0aW5jdCBjb2xvcnMpLgpJbiBhZGRpdGlvbiwgc29tZSBncm91cGluZ3Mgd2lsbCBjb250YWluIHZlcnkgZmV3IHNhbXBsZXMuCgpUaGUgcHVycG9zZSBvZiB0aGlzIG5vdGVib29rIGlzIHRvIGNyZWF0ZSBjb2xvciBwYWxldHRlcyBmb3IgdGhlIGZvbGxvd2luZzoKCiogYGJyb2FkX2hpc3RvbG9neWAgdmFsdWVzLCB3aGVyZSBhIGBicm9hZF9oaXN0b2xvZ3lgIGNvbnRhaW5zIGF0IGxlYXN0IG9uZSBgY2FuY2VyX2dyb3VwYCB3aXRoIG4gPj0gMTAKKiBgY2FuY2VyX2dyb3VwYCB2YWx1ZXMgd2l0aCBuID49IDEwCgoqKk5vdGU6IFRoaXMgaXMgdGllZCB0byBgcmVsZWFzZS12MjEtMjAyMTA4MjBgLioqCgojIyMgQmFja2dyb3VuZAoKWW91IG1heSBmaW5kIFsjMTE3NF0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzExNzQpIHRvIGJlIGhlbHBmdWwgY29udGV4dC4KCiMjIFVzYWdlCgpUaGlzIG5vdGVib29rIGNhbiBiZSBydW4gdmlhIHRoZSBjb21tYW5kIGxpbmUgZnJvbSB0aGUgdG9wIGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSBhcyBmb2xsb3dzOgoKYGBgClJzY3JpcHQgLWUgInJtYXJrZG93bjo6cmVuZGVyKCdmaWd1cmVzL21hcHBpbmctaGlzdG9sb2d5LWxhYmVscy5SbWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyBTZXQgVXAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBGaWxlcwoKYGBge3J9CiMgUGF0aCB0byBpbnB1dCBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiZGF0YSIsICJyZWxlYXNlLXYyMS0yMDIxMDgyMCIpCm91dHB1dF9kaXIgPC0gInBhbGV0dGVzIgpgYGAKCiMjIFJlYWQgaW4gbWV0YWRhdGEgCgpMZXQncyByZWFkIGluIHRoZSBgcGJ0YS1oaXN0b2xvZ2llcy50c3ZgIGZpbGUgZnJvbSBgcmVsZWFzZS12MjEtMjAyMTA4MjBgLgoKYGBge3J9Cmhpc3RvbG9naWVzX2RmIDwtCiAgcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChpbnB1dF9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpLCBndWVzc19tYXggPSAxMDAwMCkKYGBgCgojIyBJZGVudGlmeSB2YWx1ZXMgdG8gaW5jbHVkZSBpbiBwYWxldHRlcwoKV2Ugd2lsbCB1c2UgYGNhbmNlcl9ncm91cGAgd2l0aCBuID49IDEwIHRvIGd1aWRlIHdoYXQgdmFsdWVzIHRvIGluY2x1ZGUgaW4gYm90aCBvdXIgYGNhbmNlcl9ncm91cGAgYW5kIGBicm9hZF9oaXN0b2xvZ3lgIHBhbGV0dGVzLgoKYGBge3J9CmluY2x1ZGVkX2xhYmVsc19kZiA8LSBoaXN0b2xvZ2llc19kZiAlPiUgCiAgIyBFeGNsdWRlIG5vcm1hbCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZV90eXBlID09ICJUdW1vciIpICU+JQogICMgRmlsdGVyIHRvIHVuaXF1ZSBzYW1wbGUtLSJkaXNlYXNlIGxhYmVsIiBwYWlycwogIHNlbGVjdChzYW1wbGVfaWQsIAogICAgICAgICBicm9hZF9oaXN0b2xvZ3ksIAogICAgICAgICBjYW5jZXJfZ3JvdXApICU+JSAKICBkaXN0aW5jdCgpICU+JQogICMgQ291bnQgc2FtcGxlcyAoZS5nLiwgc2FtcGxlX2lkKQogIGdyb3VwX2J5KGJyb2FkX2hpc3RvbG9neSwgY2FuY2VyX2dyb3VwKSAlPiUgCiAgdGFsbHkoKSAlPiUKICAjIEFkZCBhIGNvbHVtbiBjYWxsZWQgaW5jbHVkZWQgd2hpY2ggaXMgYSBsb2dpY2FsIHRoYXQgY2FuIGJlIHVzZWQgYXMgCiAgIyBhIHNhbXBsZSBzaXplIGZpbHRlciAmIGFsc28gdG8gZHJvcCB0aGUgTkEgdmFsdWVzCiAgZmlsdGVyKG4gPj0gMTAsIAogICAgICAgICAhaXMubmEoY2FuY2VyX2dyb3VwKSkKCmluY2x1ZGVkX2xhYmVsc19kZgpgYGAKClNvIHRoZSB1bmlxdWUgdmFsdWVzIGZvciBgYnJvYWRfaGlzdG9sb2d5YCBhbmQgYGNhbmNlcl9ncm91cGAgYWJvdmUgYXJlIHdoYXQgd2UgbmVlZCB0byB0YWtlIGludG8gYWNjb3VudCBmb3Igb3VyIHBhbGV0dGUuCgojIyBDcmVhdGUgcGFsZXR0ZXMKCk91dHNpZGUgb2YgdGhpcyBub3RlYm9vaywgd2UndmUgZG9uZSBxdWl0ZSBhIGJpdCBvZiB3b3JrIHRvIGlkZW50aWZ5IHN1aXRhYmxlIHBhbGV0dGVzIHVzaW5nIGh0dHA6Ly9waHJvZ3oubmV0L2Nzcy9kaXN0aW5jdC1jb2xvcnMuaHRtbCBhcyBhIHJlZmVyZW5jZS9zdGFydGluZyBwb2ludC4KQ2hlY2sgb3V0IHRoZSBkaXNjdXNzaW9uIG9uIFsjMTE3NF0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzExNzQpIQoKIyMjIGBicm9hZF9oaXN0b2xvZ3lgCgpgYGB7cn0KYnJvYWRfaGlzdG9sb2d5X2RmIDwtIGRhdGEuZnJhbWUoCiAgYnJvYWRfaGlzdG9sb2d5ID0gYygiQmVuaWduIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICJEaWZmdXNlIGFzdHJvY3l0aWMgYW5kIG9saWdvZGVuZHJvZ2xpYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgIkVtYnJ5b25hbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAiRXBlbmR5bWFsIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICJHZXJtIGNlbGwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgIkxvdy1ncmFkZSBhc3Ryb2N5dGljIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICJNZW5pbmdpb21hIiwKICAgICAgICAgICAgICAgICAgICAgICJNZXNlbmNoeW1hbCBub24tbWVuaW5nb3RoZWxpYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgIk5ldXJvbmFsIGFuZCBtaXhlZCBuZXVyb25hbC1nbGlhbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAiVHVtb3Igb2YgY3JhbmlhbCBhbmQgcGFyYXNwaW5hbCBuZXJ2ZXMiLAogICAgICAgICAgICAgICAgICAgICAgIlR1bW9ycyBvZiBzZWxsYXIgcmVnaW9uIiksIAogIGJyb2FkX2hpc3RvbG9neV9oZXggPSBjKCIjNTkwMDI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiI2ZmODBlNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiMyMjAwNDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjMjIwMGZmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiIzAwNzRkOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiM4ZjhmYmYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjMmRiMzk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiIzdmYmYwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiM2ODU4MTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjZmZhYTAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiI2IyNTAyZCIpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIHZhbHVlIGZvciAib3RoZXIiIGhpc3RvbG9naWVzCmJyb2FkX2hpc3RvbG9neV9vdGhlcl9oZXggPC0gIiM4MDgwODAiCmBgYAoKTm93IHRvIGNyZWF0ZSBhIGxlZ2VuZCB3aXRoIGBsZWdlbmQoKWAgKGgvdCBbdGhpcyBTdGFja092ZXJmbG93IGFuc3dlcl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDg5NjY2NDUvaG93LWNhbi1pLWNyZWF0ZS1hLWxlZ2VuZC13aXRob3V0LWEtcGxvdC1pbi1yLzQ4OTY2OTI0KSkKCmBgYHtyfQpwbG90KE5VTEwsIHhheHQgPSAibiIsIHlheHQgPSAibiIsIGJ0eSA9ICJuIiwgeWxhYiA9ICIiLCB4bGFiID0gIiIsIAogICAgIHhsaW0gPSAwOjEsIHlsaW0gPSAwOjEpCmxlZ2VuZCgidG9wbGVmdCIsIAogICAgICAgbGVnZW5kID0gYyhicm9hZF9oaXN0b2xvZ3lfZGYkYnJvYWRfaGlzdG9sb2d5LCAiT3RoZXIiKSwKICAgICAgIGNvbCA9IGMoYnJvYWRfaGlzdG9sb2d5X2RmJGJyb2FkX2hpc3RvbG9neV9oZXgsIAogICAgICAgICAgICAgICBicm9hZF9oaXN0b2xvZ3lfb3RoZXJfaGV4KSwKICAgICAgIHBjaCA9IDE1LCBwdC5jZXggPSAyLCBjZXggPSAxLCBidHkgPSAibiIpCm10ZXh0KCJCcm9hZCBIaXN0b2xvZ3kiLCBhdCA9IDAuMTM1LCBjZXggPSAxLjUpCmBgYAoKIyMjIGBjYW5jZXJfZ3JvdXBgCgpUaGVyZSBhcmUgMTcgYGNhbmNlcl9ncm91cGAgdmFsdWVzIHRoYXQgd2UgbmVlZCB0byBhY2NvdW50IGZvci4KVGhlc2UgYXJlIGJlc3QgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIF9sYWJlbHNfIGluIGZpZ3VyZXMsIGJ1dCBhcmUgaW50ZW5kZWQgdG8gYWxsb3cgcmVhZGVycyB0byAidHJhY2siIGxhYmVscyBfYWNyb3NzIGZpZ3VyZXNfLgoKV2hlcmUgdGhlcmUncyBhIDE6MSBtYXBwaW5nIGJldHdlZW4gYGJyb2FkX2hpc3RvbG9neWAgYW5kIGBjYW5jZXJfZ3JvdXBgLCB0aGUgaGV4IGNvZGVzIHdpbGwgYmUgdGhlIHNhbWUuCgpgYGB7cn0KY2FuY2VyX2dyb3VwX2RmIDwtIGRhdGEuZnJhbWUoCiAgY2FuY2VyX2dyb3VwID0gYygiQ2hvcm9pZCBwbGV4dXMgcGFwaWxsb21hIiwKICAgICAgICAgICAgICAgICAgICJEaWZmdXNlIGludHJpbnNpYyBwb250aW5lIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAiRGlmZnVzZSBtaWRsaW5lIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAiSGlnaC1ncmFkZSBnbGlvbWEgYXN0cm9jeXRvbWEiLAogICAgICAgICAgICAgICAgICAgIkF0eXBpY2FsIFRlcmF0b2lkIFJoYWJkb2lkIFR1bW9yIiwKICAgICAgICAgICAgICAgICAgICJDTlMgRW1icnlvbmFsIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICJNZWR1bGxvYmxhc3RvbWEiLAogICAgICAgICAgICAgICAgICAgIkVwZW5keW1vbWEiLAogICAgICAgICAgICAgICAgICAgIlRlcmF0b21hIiwKICAgICAgICAgICAgICAgICAgICJHYW5nbGlvZ2xpb21hIiwKICAgICAgICAgICAgICAgICAgICJMb3ctZ3JhZGUgZ2xpb21hIGFzdHJvY3l0b21hIiwKICAgICAgICAgICAgICAgICAgICJNZW5pbmdpb21hIiwKICAgICAgICAgICAgICAgICAgICJFd2luZyBzYXJjb21hIiwKICAgICAgICAgICAgICAgICAgICJEeXNlbWJyeW9wbGFzdGljIG5ldXJvZXBpdGhlbGlhbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAiTmV1cm9maWJyb21hIFBsZXhpZm9ybSIsCiAgICAgICAgICAgICAgICAgICAiU2Nod2Fubm9tYSIsCiAgICAgICAgICAgICAgICAgICAiQ3JhbmlvcGhhcnluZ2lvbWEiKSwKICBjYW5jZXJfZ3JvdXBfaGV4ID0gYygiIzRkMjYzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNiZjAwOTkiLAogICAgICAgICAgICAgICAgICAgICAgICIjZmY0MGQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAiI2ZmY2NmNSIsCiAgICAgICAgICAgICAgICAgICAgICAgIiM0ZDBkODUiLAogICAgICAgICAgICAgICAgICAgICAgICIjYjA4Y2NmIiwKICAgICAgICAgICAgICAgICAgICAgICAiI2EzNDBmZiIsCiAgICAgICAgICAgICAgICAgICAgICAgIiMyMjAwZmYiLAogICAgICAgICAgICAgICAgICAgICAgICIjMDU4YWZmIiwKICAgICAgICAgICAgICAgICAgICAgICAiIzhjOGNmZiIsCiAgICAgICAgICAgICAgICAgICAgICAgIiMwMDAwODAiLAogICAgICAgICAgICAgICAgICAgICAgICIjMmRiMzk4IiwKICAgICAgICAgICAgICAgICAgICAgICAiIzlmYmY2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgIiM2MTRlMDEiLAogICAgICAgICAgICAgICAgICAgICAgICIjZTZhYzM5IiwKICAgICAgICAgICAgICAgICAgICAgICAiI2FiNzIwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNiMzMwMDAiKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQojIFZhbHVlIGZvciAib3RoZXIiIGdyb3VwcwpjYW5jZXJfZ3JvdXBfb3RoZXJfaGV4IDwtICIjYjViNWI1IgpgYGAKCkFuZCBhZ2Fpbiwgd2UnbGwgY3JlYXRlIGEgbGVnZW5kIHdpdGggYGxlZ2VuZCgpYAoKYGBge3J9CnBsb3QoTlVMTCwgeGF4dCA9ICJuIiwgeWF4dCA9ICJuIiwgYnR5ID0gIm4iLCB5bGFiID0gIiIsIHhsYWIgPSAiIiwgCiAgICAgeGxpbSA9IDA6MSwgeWxpbSA9IDA6MSkKbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICBsZWdlbmQgPSBjKGNhbmNlcl9ncm91cF9kZiRjYW5jZXJfZ3JvdXAsICJPdGhlciIpLAogICAgICAgY29sID0gYyhjYW5jZXJfZ3JvdXBfZGYkY2FuY2VyX2dyb3VwX2hleCwgY2FuY2VyX2dyb3VwX290aGVyX2hleCksCiAgICAgICBwY2ggPSAxNSwgcHQuY2V4ID0gMS41LCBjZXggPSAwLjc1LCBidHkgPSAibiIpCm10ZXh0KCJDYW5jZXIgR3JvdXAiLCBhdCA9IDAuMDYyNSwgY2V4ID0gMSkKYGBgCgojIyMgT3V0cHV0CgpXZSBjYW4gY3JlYXRlIGEgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIGJvdGggcGFsZXR0ZXMgd2l0aCBhIHNlcmllcyBvZiBsZWZ0IGpvaW5zLCB3aGVyZSB3ZSB3aWxsIHRoZW4gZmlsbCB0aGUgTkEgdmFsdWVzIHdpdGggYSBzaW5nbGUgKGdyYXkpIGhleCBjb2RlIHBlciBjb2x1bW4gKGByIGJyb2FkX2hpc3RvbG9neV9vdGhlcl9oZXhgIGZvciBgYnJvYWRfaGlzdG9sb2d5YCwgYHIgY2FuY2VyX2dyb3VwX290aGVyX2hleGAgZm9yIGBjYW5jZXJfZ3JvdXBgLikKCmBgYHtyfQpwYWxldHRlX2RmIDwtIGhpc3RvbG9naWVzX2RmICU+JQogICMgRXhjbHVkZSBub3JtYWwgc2FtcGxlcwogIGZpbHRlcihzYW1wbGVfdHlwZSA9PSAiVHVtb3IiKSAlPiUKICAjIEZpbHRlciB0byB1bmlxdWUgYnJvYWQgaGlzdG9sb2d5LS1jYW5jZXIgZ3JvdXAgcGFpcnMKICBzZWxlY3QoYnJvYWRfaGlzdG9sb2d5LCAKICAgICAgICAgY2FuY2VyX2dyb3VwKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICAjIEFkZCBicm9hZCBoaXN0b2xvZ3kgcGFsZXR0ZQogIGxlZnRfam9pbihicm9hZF9oaXN0b2xvZ3lfZGYsIGJ5ID0gImJyb2FkX2hpc3RvbG9neSIpICU+JQogICMgQWRkIGNhbmNlciBncm91cCBwYWxldHRlCiAgbGVmdF9qb2luKGNhbmNlcl9ncm91cF9kZiwgYnkgPSAiY2FuY2VyX2dyb3VwIikgJT4lCiAgIyBGaWxsIGFsbCBvdGhlciB2YWx1ZXMgd2l0aCBncmF5IGNvbG9ycwogIHJlcGxhY2VfbmEobGlzdChicm9hZF9oaXN0b2xvZ3lfaGV4ID0gYnJvYWRfaGlzdG9sb2d5X290aGVyX2hleCwKICAgICAgICAgICAgICAgICAgY2FuY2VyX2dyb3VwX2hleCA9IGNhbmNlcl9ncm91cF9vdGhlcl9oZXgpKSAlPiUKICAjIFRoZSBleGNlcHRpb24gYmVpbmcgLSBpZiBjYW5jZXJfZ3JvdXAgPT0gTkEsIHNvIHNob3VsZCBjYW5jZXJfZ3JvdXBfaGV4IQogIG11dGF0ZShjYW5jZXJfZ3JvdXBfaGV4ID0gaWZfZWxzZShpcy5uYShjYW5jZXJfZ3JvdXApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkFfY2hhcmFjdGVyXywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9oZXgpKSAlPiUKICAjIFNvcnQgYnkgYnJvYWRfaGlzdG9sb2d5IGZvciBlYXN5IGJyb3dzaW5nCiAgYXJyYW5nZShicm9hZF9oaXN0b2xvZ3kpCmBgYAoKQW5kIG5vdyBsZXQncyB0YWtlIGEgbG9vayEKCmBgYHtyfQpwYWxldHRlX2RmCmBgYAoKIyMjIEFkZCBkaXNwbGF5IG5hbWVzIGZvciBjb252ZW5pZW5jZQoKV2hlbiBtdWx0aXBsZSB2YWx1ZXMgYXJlIHVzaW5nIHRoZSBzYW1lIGNvbG9yLCBpdCBjYW4gYmUgaGVscGZ1bCB0byBoYXZlIGEgc2VwYXJhdGUgdmFsdWUgZm9yIHRoZSBsZWdlbmQsIGUuZy4sIGZvciBhbGwgYCM4MDgwODBgIGJyb2FkIGhpc3RvbG9naWVzLCB3ZSBtYXkgd2FudCB0byBkaXNwbGF5IGBPdGhlcmAuIApXZSdsbCBhZGQgYSBjb3VwbGUgY29sdW1ucyBmb3IgbGVnZW5kLW1ha2luZyBjb252ZW5pZW5jZS4KCmBgYHtyfQpwYWxldHRlX2RmIDwtIHBhbGV0dGVfZGYgJT4lCiAgbXV0YXRlKGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5ID0gaWZfZWxzZShicm9hZF9oaXN0b2xvZ3lfaGV4ID09IGJyb2FkX2hpc3RvbG9neV9vdGhlcl9oZXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJvYWRfaGlzdG9sb2d5KSwKICAgICAgICAgY2FuY2VyX2dyb3VwX2Rpc3BsYXkgPSBpZl9lbHNlKGNhbmNlcl9ncm91cF9oZXggPT0gY2FuY2VyX2dyb3VwX290aGVyX2hleCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5jZXJfZ3JvdXApKQpgYGAKCiMjIyBBZGQgYGJyb2FkX2hpc3RvbG9neV9vcmRlcmAgCgpQcmV2aW91c2x5LCB3ZSBoYWQgYSBjb25jZXB0IGtub3duIGFzIGBkaXNwbGF5X29yZGVyYCB3aGVyZSB3ZSBvcmRlcmVkIGNhdGVnb3JpZXMgYmFzZWQgb24gdGhlaXIgbnVtYmVyIG9mIHNhbXBsZXMgKGZyb20gbGFyZ2UgdG8gc21hbGwpLgpOb3cgdGhhdCB3ZSd2ZSBkcm9wcGVkIGBkaXNwbGF5X2dyb3VwYCwgbGV0J3MgYXBwbHkgdGhpcyBzYW1lIGNvbmNlcHQgdG8gYGJyb2FkX2hpc3RvbG9neWAuCgpgYGB7cn0KYnJvYWRfaGlzdG9sb2d5X29yZGVyX2RmIDwtIGhpc3RvbG9naWVzX2RmICU+JSAgCiAgIyBFeGNsdWRlIG5vcm1hbCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZV90eXBlID09ICJUdW1vciIsCiAgICAgICAgICMgT25seSBjb3VudCBoaXN0b2xvZ2llcyB0aGF0IHdlJ2xsIGhhdmUgYSBoZXggY29kZSBmb3IKICAgICAgICAgYnJvYWRfaGlzdG9sb2d5ICVpbiUgaW5jbHVkZWRfbGFiZWxzX2RmJGJyb2FkX2hpc3RvbG9neSkgJT4lCiAgIyBGaWx0ZXIgdG8gdW5pcXVlIHNhbXBsZS0tYnJvYWRfaGlzdG9sb2d5IHBhaXJzCiAgc2VsZWN0KHNhbXBsZV9pZCwgCiAgICAgICAgIGJyb2FkX2hpc3RvbG9neSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICAjIENvdW50IHNhbXBsZXMgd2l0aGluIGEgYnJvYWQgaGlzdG9sb2d5CiAgY291bnQoYnJvYWRfaGlzdG9sb2d5KSAlPiUKICAjIEFkZCBPdGhlciBwbGFjZWhvbGRlcgogIGJpbmRfcm93cyhkYXRhLmZyYW1lKGJyb2FkX2hpc3RvbG9neSA9ICJPdGhlciIsCiAgICAgICAgICAgICAgICAgICAgICAgbiA9IDAsIAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkpICU+JQogICMgUmVvcmRlciBiYXNlZCBvbiBzYW1wbGUgc2l6ZSBleGNlcHQgQmVuaWduIHR1bW9yIGFuZCBPdGhlciBzaG91bGQgY29tZSBsYXN0CiAgIyBBbmQgdGhlbiBhZGQgbnVtZXJpYyBjb2x1bW4gd2l0aCB0aGUgb3JkZXIKICBtdXRhdGUoYnJvYWRfaGlzdG9sb2d5ID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIoYnJvYWRfaGlzdG9sb2d5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZGVzYyA9IFRSVUUpICU+JQogICAgICAgICAgIGZvcmNhdHM6OmZjdF9yZWxldmVsKCJCZW5pZ24gdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWZ0ZXIgPSBJbmYpLAogICAgICAgICBicm9hZF9oaXN0b2xvZ3lfb3JkZXIgPSBhcy5udW1lcmljKGJyb2FkX2hpc3RvbG9neSkpICU+JQogICMgTm8gbG9uZ2VyIHJlcXVpcmUgdGhlIHNhbXBsZSBzaXplCiAgc2VsZWN0KC1uKQoKYnJvYWRfaGlzdG9sb2d5X29yZGVyX2RmCmBgYAoKQW5kIG5vdyB3ZSdyZSByZWFkeSB0byBhZGQgdGhpcyB0byB0aGUgcGFsZXR0ZSBkYXRhIGZyYW1lLgoKYGBge3J9CnBhbGV0dGVfZGYgPC0gcGFsZXR0ZV9kZiAlPiUKICBsZWZ0X2pvaW4oYnJvYWRfaGlzdG9sb2d5X29yZGVyX2RmLCAKICAgICAgICAgICAgYnkgPSBjKCJicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSIgPSAiYnJvYWRfaGlzdG9sb2d5IikpCmBgYAoKIyMjIEFkZCBgb25jb3ByaW50X2dyb3VwYCBhbmQgYG9uY29wcmludF9oZXhgCgpGb3IgbW9zdCBwbG90cyB0aGF0IG1ha2UgdXNlIG9mIHRoZSBgY2FuY2VyX2dyb3VwYCBwYWxldHRlLCBzdWNoIGFzIGEgYm94IG9yIHZpb2xpbiBwbG90LCB3ZSB3aWxsIHJlbHkgaGVhdmlseSBvbiBsYWJlbHMgYW5kIHRoZXJlZm9yZSB1c2luZyB0aGUgZ3JheSBoZXggY29kZSBmb3IgbXVsdGlwbGUgZ3JvdXBzIHdpbGwgbm90IGJlIGEgcHJvYmxlbS4KCldlIHdpbGwgaGF2ZSBmb3VyIHBhbmVscyBvZiBpbmRpdmlkdWFsIG9uY29wcmludHMsIHdoZXJlIG1hbnkgYGJyb2FkX2hpc3RvbG9neWAgdmFsdWVzIHdpbGwgZ2V0IGdyb3VwZWQgdG9nZXRoZXIgaW50byB0aGUgYE90aGVyIENOU2AgcGFuZWwgd2hpY2ggeW91IGNhbiBzZWUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL09wZW5QQlRBLWFuYWx5c2lzL2Jsb2IvZDMxYzkyN2EyNzgxM2VjMGI4MDMyZmJlNzY4MDAyZjMxNzIzNjM2Zi9hbmFseXNlcy9vbmNvcHJpbnQtbGFuZHNjYXBlLzAyLXBsb3Qtb25jb3ByaW50LlIjTDE4MSkuCldlIGNhbiBtb3ZlIHRoaXMgaW5mb3JtYXRpb24gaW50byBvdXIgcGFsZXR0ZSBkYXRhIGZyYW1lLgoKYGBge3J9CiMgVGFrZW4gZnJvbSB0aGUgY3VycmVudCBwbG90IG9uY29wcmludCBzY3JpcHQgYXMgb2YgdGhlIHdyaXRpbmcgb2YgdGhpcwojIFNlZSBwZXJtYWxpbmsgYWJvdmUKb3RoZXJfY25zX2Jyb2FkX2hpc3RvbG9naWVzIDwtIGMoCiAgIkVwZW5keW1hbCB0dW1vciIsCiAgIlR1bW9ycyBvZiBzZWxsYXIgcmVnaW9uIiwKICAiTmV1cm9uYWwgYW5kIG1peGVkIG5ldXJvbmFsLWdsaWFsIHR1bW9yIiwKICAiVHVtb3Igb2YgY3JhbmlhbCBhbmQgcGFyYXNwaW5hbCBuZXJ2ZXMiLAogICJNZW5pbmdpb21hIiwKICAiTWVzZW5jaHltYWwgbm9uLW1lbmluZ290aGVsaWFsIHR1bW9yIiwKICAiR2VybSBjZWxsIHR1bW9yIiwKICAiQ2hvcm9pZCBwbGV4dXMgdHVtb3IiLAogICJIaXN0aW9jeXRpYyB0dW1vciIsCiAgIlR1bW9yIG9mIHBpbmVhbCByZWdpb24iLAogICJNZXRhc3RhdGljIHR1bW9ycyIsCiAgIk90aGVyIGFzdHJvY3l0aWMgdHVtb3IiLAogICJMeW1waG9tYSIsCiAgIk1lbGFub2N5dGljIHR1bW9yIiwKICAiT3RoZXIgdHVtb3IiCikKCnBhbGV0dGVfZGYgPC0gcGFsZXR0ZV9kZiAlPiUKICBtdXRhdGUob25jb3ByaW50X2dyb3VwID0gY2FzZV93aGVuKAogICAgYnJvYWRfaGlzdG9sb2d5ICVpbiUgb3RoZXJfY25zX2Jyb2FkX2hpc3RvbG9naWVzIH4gIk90aGVyIENOUyIsCiAgICBicm9hZF9oaXN0b2xvZ3kgJWluJSBjKAogICAgICAiTG93LWdyYWRlIGFzdHJvY3l0aWMgdHVtb3IiLAogICAgICAiRW1icnlvbmFsIHR1bW9yIiwKICAgICAgIkRpZmZ1c2UgYXN0cm9jeXRpYyBhbmQgb2xpZ29kZW5kcm9nbGlhbCB0dW1vciIKICAgICkgfiBicm9hZF9oaXN0b2xvZ3ksCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXwogICkpCmBgYAoKRm9yIGNhbmNlciBncm91cHMgdGhhdCBkbyBub3QgZ2V0IHRoZWlyIG93biBoZXggY29kZSBmb3IgZGlzcGxheSAoaS5lLiwgZHVlIHRvIHNtYWxsIHNhbXBsZSBzaXplcyksIHdlJ2xsIHVzZSBhIHNlbGVjdGlvbiBvZiBncmV5IGNvbG9ycyBhcyBhIHBhbGV0dGUgYW5kIHJlbHkgaGVhdmlseSBvbiB0aGUgb3JkZXJpbmcgb2YgdGhlIE9uY29QcmludCBsZWdlbmQuClVuZm9ydHVuYXRlbHkgdGhlcmUgYXJlIG92ZXIgMjAgIk90aGVyIENOUyIgY2FuY2VyIGdyb3VwcyB0aGF0IG1lZXQgdGhpcyBjcml0ZXJpb24sIHNvIGl0IGlzIG5vdCBmZWFzaWJsZSB0byBoYXZlIGEgY29sb3IgZm9yIGVhY2ggb2YgdGhlbSBhbmQgdGhleSB3aWxsIG5vdCBiZSBpbmNsdWRlZCBpbiB0aGUgIk90aGVyIENOUyIgT25jb1ByaW50LgoKYGBge3J9CmdyZXlzX2RmIDwtIHBhbGV0dGVfZGYgJT4lCiAgZmlsdGVyKGNhbmNlcl9ncm91cF9kaXNwbGF5ID09ICJPdGhlciIsCiAgICAgICAgICFpcy5uYShvbmNvcHJpbnRfZ3JvdXApLAogICAgICAgICBvbmNvcHJpbnRfZ3JvdXAgIT0gIk90aGVyIENOUyIpCgojIFNhbXBsZSB0aGUgZ3JleXMgc2VxdWVudGlhbCBwYWxldHRlIGZyb20gY29sb3IgYnJld2VyCnNldC5zZWVkKDIwMjEpCmdyZXlzX2RmIDwtIGdyZXlzX2RmICU+JQogIG11dGF0ZShvbmNvcHJpbnRfaGV4ID0gc2FtcGxlKGJyZXdlci5wYWwobnJvdyhncmV5c19kZiksICJHcmV5cyIpKSkKYGBgCgpBZGQgYG9uY29wcmludF9oZXhgICYgYG9uY29wcmludF9pbmNsdWRlYCwgdGhlIGxhdHRlciB3aWxsIGJlIGBGQUxTRWAgd2hlbiB0aGUgZm9ybWVyIGlzIGBOQWAuCgpBZGQgYG9uY29wcmludF9oZXhgIGZvciBhbGwgb3RoZXIgY2FuY2VyIGdyb3VwcyBhbmQgYG9uY29wcmludF9pbmNsdWRlYC4KVGhlIGxhdHRlciB3aWxsIGJlIEZBTFNFIHdoZW4gdGhlIGZvcm1lciBpcyBOQS4KCmBgYHtyfQpwYWxldHRlX2RmIDwtIHBhbGV0dGVfZGYgJT4lCiAgbGVmdF9qb2luKGdyZXlzX2RmKSAlPiUKICAjIFdoZW4gdGhlcmUncyBhbiBvbmNvcHJpbnQgZ3JvdXAgYW5kIGEgc3BlY2lmaWMgY2FuY2VyIGdyb3VwIGRpc3BsYXkgY29sb3IsCiAgIyB1c2UgdGhlIGNhbmNlciBncm91cCBkaXNwbGF5IGNvbG9yIGFzIHRoZSBvbmNvcHJpbnQgY29sb3IKICBtdXRhdGUob25jb3ByaW50X2hleCA9IGlmX2Vsc2UoCiAgICBjYW5jZXJfZ3JvdXBfaGV4ICE9IGNhbmNlcl9ncm91cF9vdGhlcl9oZXggJiAhaXMubmEob25jb3ByaW50X2dyb3VwKSwKICAgIGNhbmNlcl9ncm91cF9oZXgsCiAgICBvbmNvcHJpbnRfaGV4CiAgKSwKICAjIE9ubHkgd2hlbiB0aGVyZSdzIGEgc3BlY2lmaWMgb25jb3ByaW50IGNvbG9yIC0tIGV2ZW4gaWYgdGhhdCBpcyBhIGdyZXkgCiAgIyBzZWxlY3RlZCBvbmx5IGZvciB0aGUgb25jb3ByaW50IC0tIHdpbGwgYSBjYW5jZXIgZ3JvdXAgYmUgaW5jbHVkZWQgaW4gdGhlCiAgIyBvbmNvcHJpbnQKICBvbmNvcHJpbnRfaW5jbHVkZSA9IGlmX2Vsc2UoCiAgICBpcy5uYShvbmNvcHJpbnRfaGV4KSwKICAgIEZBTFNFLAogICAgVFJVRQogICkpCmBgYAoKIyMjIFNhdmUgdG8gVFNWIAoKYGBge3J9CnJlYWRyOjp3cml0ZV90c3YocGFsZXR0ZV9kZiwgCiAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKG91dHB1dF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYnJvYWRfaGlzdG9sb2d5X2NhbmNlcl9ncm91cF9wYWxldHRlLnRzdiIpKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==
diff --git a/figures/palettes/broad_histology_cancer_group_palette.tsv b/figures/palettes/broad_histology_cancer_group_palette.tsv new file mode 100644 index 0000000000..4a017f9d10 --- /dev/null +++ b/figures/palettes/broad_histology_cancer_group_palette.tsv @@ -0,0 +1,62 @@ +broad_histology cancer_group broad_histology_hex cancer_group_hex broad_histology_display cancer_group_display broad_histology_order oncoprint_group oncoprint_hex oncoprint_include +Benign tumor NA #590024 NA Benign tumor NA 11 NA NA FALSE +Benign tumor Choroid plexus papilloma #590024 #4d2635 Benign tumor Choroid plexus papilloma 11 NA NA FALSE +Benign tumor Atypical choroid plexus papilloma #590024 #b5b5b5 Benign tumor Other 11 NA NA FALSE +Benign tumor Adenoma #590024 #b5b5b5 Benign tumor Other 11 NA NA FALSE +Chordoma Chordoma #808080 #b5b5b5 Other Other 12 NA NA FALSE +Choroid plexus tumor Choroid plexus carcinoma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Choroid plexus tumor Choroid plexus cyst #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Diffuse astrocytic and oligodendroglial tumor High-grade glioma astrocytoma #ff80e5 #ffccf5 Diffuse astrocytic and oligodendroglial tumor High-grade glioma astrocytoma 2 Diffuse astrocytic and oligodendroglial tumor #ffccf5 TRUE +Diffuse astrocytic and oligodendroglial tumor Diffuse midline glioma #ff80e5 #ff40d9 Diffuse astrocytic and oligodendroglial tumor Diffuse midline glioma 2 Diffuse astrocytic and oligodendroglial tumor #ff40d9 TRUE +Diffuse astrocytic and oligodendroglial tumor Diffuse intrinsic pontine glioma #ff80e5 #bf0099 Diffuse astrocytic and oligodendroglial tumor Diffuse intrinsic pontine glioma 2 Diffuse astrocytic and oligodendroglial tumor #bf0099 TRUE +Diffuse astrocytic and oligodendroglial tumor Oligodendroglioma #ff80e5 #b5b5b5 Diffuse astrocytic and oligodendroglial tumor Other 2 Diffuse astrocytic and oligodendroglial tumor #525252 TRUE +Embryonal tumor Medulloblastoma #220040 #a340ff Embryonal tumor Medulloblastoma 3 Embryonal tumor #a340ff TRUE +Embryonal tumor CNS Embryonal tumor #220040 #b08ccf Embryonal tumor CNS Embryonal tumor 3 Embryonal tumor #b08ccf TRUE +Embryonal tumor Atypical Teratoid Rhabdoid Tumor #220040 #4d0d85 Embryonal tumor Atypical Teratoid Rhabdoid Tumor 3 Embryonal tumor #4d0d85 TRUE +Embryonal tumor Embryonal tumor with multilayer rosettes #220040 #b5b5b5 Embryonal tumor Other 3 Embryonal tumor #737373 TRUE +Embryonal tumor Ganglioneuroblastoma #220040 #b5b5b5 Embryonal tumor Other 3 Embryonal tumor #252525 TRUE +Embryonal tumor CNS neuroblastoma #220040 #b5b5b5 Embryonal tumor Other 3 Embryonal tumor #F0F0F0 TRUE +Embryonal tumor Neuroblastoma #220040 #b5b5b5 Embryonal tumor Other 3 Embryonal tumor #BDBDBD TRUE +Ependymal tumor Ependymoma #2200ff #2200ff Ependymal tumor Ependymoma 4 Other CNS #2200ff TRUE +Germ cell tumor Teratoma #0074d9 #058aff Germ cell tumor Teratoma 10 Other CNS #058aff TRUE +Germ cell tumor Germinoma #0074d9 #b5b5b5 Germ cell tumor Other 10 Other CNS NA FALSE +Germ cell tumor Germinoma-Teratoma #0074d9 #b5b5b5 Germ cell tumor Other 10 Other CNS NA FALSE +Histiocytic tumor Rosai-Dorfman disease #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Histiocytic tumor Langerhans Cell histiocytosis #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Histiocytic tumor Juvenile xanthogranuloma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Low-grade astrocytic tumor Low-grade glioma astrocytoma #8f8fbf #000080 Low-grade astrocytic tumor Low-grade glioma astrocytoma 1 Low-grade astrocytic tumor #000080 TRUE +Low-grade astrocytic tumor Ganglioglioma #8f8fbf #8c8cff Low-grade astrocytic tumor Ganglioglioma 1 Low-grade astrocytic tumor #8c8cff TRUE +Low-grade astrocytic tumor Subependymal Giant Cell Astrocytoma #8f8fbf #b5b5b5 Low-grade astrocytic tumor Other 1 Low-grade astrocytic tumor #969696 TRUE +Low-grade astrocytic tumor Diffuse fibrillary astrocytoma #8f8fbf #b5b5b5 Low-grade astrocytic tumor Other 1 Low-grade astrocytic tumor #000000 TRUE +Low-grade astrocytic tumor Pleomorphic xanthoastrocytoma #8f8fbf #b5b5b5 Low-grade astrocytic tumor Other 1 Low-grade astrocytic tumor #D9D9D9 TRUE +Low-grade astrocytic tumor Pilocytic astrocytoma #8f8fbf #b5b5b5 Low-grade astrocytic tumor Other 1 Low-grade astrocytic tumor #FFFFFF TRUE +Lymphoma CNS Burkitt's lymphoma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Melanocytic tumor Melanocytic tumor #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Meningioma Meningioma #2db398 #2db398 Meningioma Meningioma 8 Other CNS #2db398 TRUE +Mesenchymal non-meningothelial tumor Sarcoma #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Mesenchymal non-meningothelial tumor Ewing sarcoma #7fbf00 #9fbf60 Mesenchymal non-meningothelial tumor Ewing sarcoma 9 Other CNS #9fbf60 TRUE +Mesenchymal non-meningothelial tumor Hemangioblastoma #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Mesenchymal non-meningothelial tumor Rhabdomyosarcoma #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Mesenchymal non-meningothelial tumor Fibromyxoid lesion #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Mesenchymal non-meningothelial tumor Cavernoma #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Mesenchymal non-meningothelial tumor Myofibroblastoma #7fbf00 #b5b5b5 Mesenchymal non-meningothelial tumor Other 9 Other CNS NA FALSE +Metastatic tumors Metastatic secondary tumors-Neuroblastoma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Metastatic tumors Metastatic secondary tumors #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Diffuse leptomeningeal glioneuronal tumor #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Dysembryoplastic neuroepithelial tumor #685815 #614e01 Neuronal and mixed neuronal-glial tumor Dysembryoplastic neuroepithelial tumor 6 Other CNS #614e01 TRUE +Neuronal and mixed neuronal-glial tumor Rosette-forming glioneuronal tumor #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Dysplasia Gliosis-Glial-neuronal tumor NOS #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Glial-neuronal tumor NOS #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Neurocytoma #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Neuronal and mixed neuronal-glial tumor Desmoplastic infantile astrocytoma and ganglioglioma #685815 #b5b5b5 Neuronal and mixed neuronal-glial tumor Other 6 Other CNS NA FALSE +Non-CNS tumor Myxoid spindle cell tumor #808080 #b5b5b5 Other Other 12 NA NA FALSE +Non-tumor Epilepsy #808080 #b5b5b5 Other Other 12 NA NA FALSE +Non-tumor Arteriovenous malformation #808080 #b5b5b5 Other Other 12 NA NA FALSE +Non-tumor Reactive connective tissue #808080 #b5b5b5 Other Other 12 NA NA FALSE +Other tumor Ganglioneuroma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Pre-cancerous lesion NA #808080 NA Other NA 12 NA NA FALSE +Tumor of cranial and paraspinal nerves Schwannoma #ffaa00 #ab7200 Tumor of cranial and paraspinal nerves Schwannoma 5 Other CNS #ab7200 TRUE +Tumor of cranial and paraspinal nerves Neurofibroma Plexiform #ffaa00 #e6ac39 Tumor of cranial and paraspinal nerves Neurofibroma Plexiform 5 Other CNS #e6ac39 TRUE +Tumor of cranial and paraspinal nerves Malignant peripheral nerve sheath tumor #ffaa00 #b5b5b5 Tumor of cranial and paraspinal nerves Other 5 Other CNS NA FALSE +Tumor of pineal region Pineoblastoma #808080 #b5b5b5 Other Other 12 Other CNS NA FALSE +Tumors of sellar region Craniopharyngioma #b2502d #b33000 Tumors of sellar region Craniopharyngioma 7 Other CNS #b33000 TRUE